<script data-pm-proxy="intercept"></script><?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Web Development Newsletter by Packt]]></title><description><![CDATA[Join 38,000+ web developers and designers who get weekly insights on web development, web design, frameworks, and tools. Stay updated on JavaScript, React, Angular, Node, and AI-powered development - straight to your feed.]]></description><link>https://packtwebdevpro.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!-Epb!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c609e43-4006-496a-9adf-84620ff6b584_500x500.png</url><title>Web Development Newsletter by Packt</title><link>https://packtwebdevpro.substack.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 02 Sep 2026 05:09:25 GMT</lastBuildDate><atom:link href="/__u/packtwebdevpro.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Packt WebDevPro]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[webdevpro@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[webdevpro@substack.com]]></itunes:email><itunes:name><![CDATA[Packt]]></itunes:name></itunes:owner><itunes:author><![CDATA[Packt]]></itunes:author><googleplay:owner><![CDATA[webdevpro@substack.com]]></googleplay:owner><googleplay:email><![CDATA[webdevpro@substack.com]]></googleplay:email><googleplay:author><![CDATA[Packt]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[#155: Why the Harness Matters More Than the Model]]></title><description><![CDATA[No framework, no SDK, just two ways to call an LLM from plain PHP]]></description><link>https://packtwebdevpro.substack.com/p/155-why-the-harness-matters-more</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/155-why-the-harness-matters-more</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 31 Aug 2026 14:03:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!kN25!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span><a href="https://www.linkedin.com/in/dbierer/"><span>Doug Biere</span>r</a></strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!kN25!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 424w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 848w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 1272w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!kN25!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png" width="251" height="271.87524752475247" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:547,&quot;width&quot;:505,&quot;resizeWidth&quot;:251,&quot;bytes&quot;:468127,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/213104865?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 424w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 848w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 1272w, /__u/substackcdn.com/image/fetch/$s_!kN25!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc4a3464d-9557-4ef0-9c3a-eb6ecc8cefc5_505x547.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><strong><a href="https://www.linkedin.com/in/dbierer/">Doug</a></strong><a href="https://www.linkedin.com/in/dbierer/"> </a>has been involved in the computer industry since the advent of microcomputers. His first program was written on a PDP-8 with a whopping 4K of RAM. Since that time, Doug has written applications for customers on four different continents, in over a dozen languages, including Assembler, FORTH, C, C++, Prolog, and Perl, as well as Java, Python, and PHP. He also has extensive experience in systems administration, networking, and database administration, including MySQL, Oracle, PostgreSQL, and MongoDB.</p></div><p><span>It&#8217;s absurdly easy to turn your PHP website into an AI-assisted powerhouse. No new dependencies, no extra service to stand up, just the PHP you already have installed. In this brief article, we&#8217;ll show you two ways to make a call to a GenAI platform.</span></p><h1>What is a REST request?</h1><p><span>A REST request uses standard HTTP methods (</span><code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, and <code>DELETE</code><span>) to perform operations on resources identified by URLs. It is stateless, meaning each request contains all necessary information, and typically exchanges data in JSON or XML format. Both Streams (the standard PHP I/O functions) and the cURL extension can be used to make REST requests to external web services. In this recipe, we show you how to make requests to a remote GenAI REST service using both techniques.</span></p><p><span>Without further ado, let&#8217;s get right to it!</span></p><h1>Identifying the goal</h1><p><span>Before going any further, it&#8217;s important to identify our goal. For the purposes of this article, we&#8217;re going to make a REST API call to the </span><a href="https://openrouter.ai/"><span>openrouter.ai</span></a><span> platform. The reason this platform is a great choice is because its single API can be used against any of the ever-changing sea of GenAI models available at any given moment.</span></p><p><span>Our ultimate goal is to emulate the following command line request:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;523be987-8a8b-4988-b91b-38681683db07&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">curl https://openrouter.ai/api/v1/chat/completions 
-H &#8220;Content-Type: application/json&#8221; 
-H &#8220;Authorization: Bearer $OPENROUTER_API_KEY&#8221; 
-d &#8216;{ &#8220;model&#8221;: &#8220;mistralai/mistral-nemo&#8221;, &#8220;messages&#8221;: [ { &#8220;role&#8221;: &#8220;system&#8221;, &#8220;content&#8221;: &#8220;Just give me the name of the capital without any additional explanation.&#8221;}, { &#8220;role&#8221;: &#8220;user&#8221;, &#8220;content&#8221;: &#8220;What is the capital of France?&#8221; } ] }&#8217;</code></pre></div><h1>Making a REST request using PHP Streams</h1><p><span>PHP </span><a href="https://www.php.net/streams"><span>Streams</span></a><span> is a unified abstraction layer that lets you work with files, network connections, and data using the standard I/O functions (e.g., fopen, fread, fwrite). It handles protocols like HTTP, FTP, and data compression, operating on top of what the PHP documentation refers to as &#8220;</span><a href="https://www.php.net/manual/en/wrappers.php"><span>wrappers</span></a><span>&#8220;.</span></p><p><span>In this simple example, we&#8217;ll create a class </span><em><strong><span>SimpleAPICall</span></strong></em><span> that contains two methods that both connect to a GenAI API endpoint. The first method, </span><code>streamsConnect()</code><span>, uses PHP Streams to make the call. The other method, </span><code>curlConnect()</code><span>, described in the next section, uses the PHP cURL extension to make the call.</span></p><p><span>We start with a class opener that defines a couple of constants along with a method that returns the appropriate headers. You&#8217;ll note that our GenAI API key is stored in an environment variable </span><code>OPENROUTER_API_KEY</code><span>:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;618c2e10-6064-4649-b0fe-0b97dd3d0056&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php 
class SimpleAPICall { 
 const API_GET_URL = &#8216;https://openrouter.ai/api/v1/chat/completions&#8217;; 
 const API_ERROR = &#8216;ERROR: api transmission error&#8217;; 
 public static function getHeaders() { 
 return [ &#8216;Content-Type: application/json&#8217;, 
 &#8216;Authorization: Bearer &#8216; . getenv(&#8217;OPENROUTER_API_KEY&#8217;), ]; 
 }</code></pre></div><p><span>Next, we define the method to connect using Streams. The most important aspect of this code is setting the </span><em><span>context</span></em><span>. This instructs </span><code>file_get_contents()</code><span> to send out the metadata defined in the context when making the API call.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;3eddbebc-5892-4bc2-bbf9-30551d478b8c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">public static function streamsConnect(array $data) : array
{
 try {
 $headers = self::getHeaders();
 $options = [
 &#8216;http&#8217; =&gt; [
 &#8216;method&#8217; =&gt; &#8216;POST&#8217;,
 &#8216;header&#8217;=&gt; implode(&#8221;\r\n&#8221;, $headers),
 &#8216;content&#8217; =&gt; json_encode($data, JSON_THROW_ON_ERROR),
 ]
 ];
 $context = stream_context_create($options);
 $text = file_get_contents(static::API_GET_URL, context: $context);
 $data = json_decode($text, TRUE, JSON_THROW_ON_ERROR);
 if (!empty($data[&#8217;error&#8217;])) {
 $response = [&#8217;success&#8217; =&gt; false, 
 &#8216;data&#8217; =&gt; ($data[&#8217;error&#8217;][&#8217;message&#8217;] ?? self::API_ERROR) 
 . &#8216; [&#8217; . ($data[&#8217;error&#8217;][&#8217;code&#8217;] ?? 400) . &#8216;]&#8217;];
 } else {
 $response = [&#8217;success&#8217; =&gt; true, &#8216;data&#8217; =&gt; $data];
 }
 } catch (Throwable $t) {
 error_log(__METHOD__ . &#8216;:&#8217; . $t-&gt;getMessage());
 $response = [&#8217;success&#8217; =&gt; false, &#8216;data&#8217; =&gt; static::API_ERROR];
 }
 return $response;
}</code></pre></div><p><span>Worth pausing on that context array for a second: everything you&#8217;d normally hand to curl on the command line (the method, the headers, the body) gets folded into one nested array and handed to </span><code>stream_context_create()</code><span>. </span><code>file_get_contents() </code><span>then treats the API endpoint exactly like any other URL, which is the whole point of Streams: HTTP is just another wrapper, not a special case.</span></p><p><span>Normally we&#8217;d return a JSON response, but in this case we&#8217;ll stick to an array response to keep things simple.</span></p><h1>Making the call using the cURL extension</h1><p><span>Next let&#8217;s have a look at making the request using cURL. To this end, we define a method </span><code>curlConnect()</code><span> as shown here:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;ed20ad0d-f991-4c4f-83b9-d0b8914cb043&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">public static function curlConnect(array $data) : array { 
 $send = json_encode($data, JSON_THROW_ON_ERROR); 
 $ch = curl_init(static::API_GET_URL); 
 curl_setopt_array($ch, [ 
 CURLOPT_HTTPHEADER =&gt; self::getHeaders(), 
 CURLOPT_CUSTOMREQUEST =&gt; &#8216;POST&#8217;, 
 CURLOPT_POSTFIELDS =&gt; $send, 
 CURLOPT_RETURNTRANSFER=&gt; true, 
 CURLOPT_HEADER=&gt; false, 
 CURLOPT_TIMEOUT=&gt; 60, 
 // to make this work locally, set both CURLOPT_SSL* options to &#8220;false&#8221; 
 CURLOPT_SSL_VERIFYPEER =&gt; false, 
 CURLOPT_SSL_VERIFYHOST =&gt; false, ]); 
 try { 
 $text = curl_exec($ch); 
 $data = json_decode($text, TRUE, JSON_THROW_ON_ERROR); 
 if (!empty($data[&#8217;error&#8217;])) { 
 $response = [&#8217;success&#8217; =&gt; false, 
 &#8216;data&#8217; =&gt; ($data[&#8217;error&#8217;][&#8217;message&#8217;] ?? self::API_ERROR) 
 . &#8216; [&#8217; . ($data[&#8217;error&#8217;][&#8217;code&#8217;] ?? 400) . &#8216;]&#8217;]; 
 } else { 
 $response = [&#8217;success&#8217; =&gt; true, &#8216;data&#8217; =&gt; $data]; 
 } 
 } catch (Throwable $t) { 
 error_log(METHOD . &#8216;:&#8217; . $t-&gt;getMessage()); 
 $response = [&#8217;success&#8217; =&gt; false, &#8216;data&#8217; =&gt; static::API_ERROR]; 
 } finally { 
 curl_close($ch); 
 } 
 return $response; 
}}</code></pre></div><p><span>Notice how much of that is boilerplate you&#8217;ll reuse verbatim on every call: the headers, the POST method, the timeout. The only thing that changes between requests is $data, which is exactly why wrapping both approaches in a class like this pays off the moment you&#8217;re calling the API more than once.</span></p><p><span>And that ends the class.</span></p><h1>Making the call</h1><p><span>In order to make the call we set up a simple test program as shown here:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;php&quot;,&quot;nodeId&quot;:&quot;dde3b43a-2917-4bb4-b3fa-0d0005360570&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-php">&lt;?php
$data = [
&#8216;model&#8217;=&gt; &#8216;mistralai/mistral-nemo&#8217;,
&#8216;messages&#8217;=&gt; [
[
&#8216;role&#8217;=&gt; &#8216;system&#8217;,
&#8216;content&#8217;=&gt; &#8216;Only give me the name of the capital.&#8217;
],
[
&#8216;role&#8217;=&gt; &#8216;user&#8217;,
&#8216;content&#8217;=&gt; &#8216;What is the capital of France?&#8217;
]
]
];
$result = SimpleAPICall::streamsConnect($data);
print_r($result);
echo PHP_EOL;
$result = SimpleAPICall::curlConnect($data);
print_r($result);</code></pre></div><p><span>We won&#8217;t show the output here to conserve space, but suffice it to say that the result is an array with values associated with this platform, including usage information (good to know if you&#8217;re concerned with cost control!), any error information, and, of course, the final answer: Paris. Have fun, and enjoy coding!</span></p><blockquote><p><strong><span>IMPORTANT</span></strong><span>: GenAI model selection is </span><em><span>extremely important</span></em><span>. Just the other day, I forgot to reset the default fallback. Later, to my regret, I was hit with a $43 bill after just one hour of use because the fallback was Claude Opus, one of the most expensive models!</span></p></blockquote><div class="pullquote"><p>The information for this article was drawn from a recipe titled &#8220;Using standard I/O and PHP cURL to issue REST requests,&#8221; from the book <em><strong><a href="https://www.amazon.com/PHP-Programming-Era-applications-production-ready/dp/1835889921">PHP Programming in the AI Era</a></strong></em>.</p></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#127912; <strong><a href="https://linear.app/now/styling-linear-for-the-future-stylex">Linear's migration from styled-components to StyleX</a>: </strong>After 1,000+ PRs, Linear ditched runtime CSS-in-JS for Meta's build-time compiled <a href="https://stylexjs.com/">StyleX</a>, betting that atomic, contract-enforced styles are safer for agents to touch than the old freeform cascade.</p><p>&#129408; <strong><a href="https://bun.com/blog/bun-v1.4">Bun ships 1.4, and it&#8217;s rewritten in Rust</a>:</strong> Bun&#8217;s Zig-to-Rust rewrite finally landed, and it&#8217;s not just a fresh coat of paint. Idle CPU is down 5x, memory up to 35% lower, Linux startup 2x faster, plus new toys like <code>Bun.Image</code>, <code>Bun.WebView</code> (a headless browser with zero installs), and a built-in React Compiler. Ambitious doesn&#8217;t begin to cover it.</p><p>&#128221; <strong><a href="https://blog.cloudflare.com/emdash-wordpress/">How Cloudflare moved its own blog onto its new WordPress rival</a>:</strong> Cloudflare just launched EmDash, a sandboxed-plugin CMS built on Astro, and promptly used it to migrate blog.cloudflare.com. Naturally, WordPress&#8217;s Matt Mullenweg wasn&#8217;t impressed, calling it a play to &#8220;sell more Cloudflare services.&#8221; Fair, but that&#8217;s kind of always the pitch with platform vendors.</p><p>&#128172; <strong><a href="https://www.reddit.com/r/nextjs/comments/1vrq0tp/were_the_nextjs_team_ask_us_anything/">The Next.js team did a Reddit AMA, and it&#8217;s worth the scroll</a>:</strong> No fluff here, just the team fielding real questions from the trenches. If you use Next.js and have opinions (you do), this thread is a rare unfiltered look at how the sausage gets made.</p><p>&#129414; <strong><a href="https://www.allthingsdistributed.com/2026/08/duckdb-and-the-changing-physics-of-analytics.html">DuckDB&#8217;s creators are joining AWS</a>:</strong> Werner Vogels hands the mic to Andy Warfield, who makes the case that single-machine hardware has quietly gotten so powerful that a huge chunk of &#8220;big data&#8221; work never needed a cluster in the first place. He also explains why DuckLabs is now joining AWS as a subsidiary while staying open source. A genuinely fun read on how the physics of computing keeps rewriting database design.</p><p>&#128421;&#65039; <strong><a href="https://runtimewire.com/article/openai-is-building-an-interface-platform-inside-chatgpt">OpenAI is quietly building a whole interface platform inside ChatGPT</a>:</strong> Reverse-engineering of the Codex client turned up &#8220;GenUI,&#8221; a server-directed system that can keep updating parts of an answer after it&#8217;s been delivered, plus 467 versioned &#8220;Learning Block&#8221; modules covering everything from Bayes&#8217; theorem to suturing technique. OpenAI hasn&#8217;t confirmed any of it, but the receipts (hashes, endpoints, and all) are pretty compelling.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p>&#128027; <strong><a href="https://blog.tymscar.com/posts/howtoreportabug/">How to report a bug so it actually gets fixed</a>:</strong> A genuinely great walkthrough of turning a mystifying Netty <code>ByteBuf</code> leak into a Microsoft-shipped fix within weeks, not months. The trick is treating bug reports as a craft (bisect versions, shrink to a minimal repro, do the archaeology) rather than a complaint. Worth bookmarking for the next time something&#8217;s on fire.</p><p>&#9201;&#65039; <strong><a href="https://infrequently.org/2026/08/notes-on-performance-remediation-strategies/">Notes on performance remediation strategies</a>:</strong> Alex Russell&#8217;s latest on the never-ending fight against bloated, slow websites. Expect his usual mix of hard data and impatience with the industry&#8217;s excuses. Note: this one blocks bots, so go read it directly.</p><p>&#127539; <strong><a href="https://plicara.ai/research/agent-skill-languages/">What language are agent skills actually written in?</a>:</strong> A dig through 3.8 million SKILL.md files on GitHub finds 14.3% aren&#8217;t in English, and that share is climbing fast, led by Chinese and European-language skills. The twist: non-English skills get revised more but copied less, suggesting good work is getting made outside the Anglosphere and then just not found.</p><p>&#127897;&#65039; <strong><a href="https://www.youtube.com/watch?v=cywK3XYYJ2o">Anders Hejlsberg on rewriting TypeScript for 10x speed</a>:</strong> The creator of TypeScript and C# on why the compiler moved to Go, why JavaScript keeps winning, and his grounded take on AI's real impact on software engineering.</p><p>&#128268; <strong><a href="https://dashbit.co/blog/websockets-vs-sse">WebSockets vs. SSE should really be about ordering, not vibes</a>:</strong> Jos&#233; Valim&#8217;s rebuttal to the &#8220;just use SSE, latency&#8217;s the same&#8221; HN take. The real issue is that splitting updates across two streams invites race conditions your UI won&#8217;t recover from without a refresh, and bidirectional WebSockets sidestep this by construction. A sharp, opinionated read for anyone architecting real-time UIs.</p><h1 style="text-align: center;"><strong>Tool of the Week</strong></h1><p>&#9000;&#65039; <strong><a href="https://termdom.org/">TermDOM: build terminal apps with actual HTML, CSS, and the DOM</a>:</strong> No proprietary Box/List/Screen widgets to learn. TermDOM implements a real, spec-compliant DOM and CSSOM and paints it to ANSI escape sequences, meaning flexbox, <code>:focus</code> styles, and even unmodified browser libraries (Prism.js syntax highlighting!) just work in your terminal. Delightfully weird and clearly well-built.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#154: Why the Harness Matters More Than the Model]]></title><description><![CDATA[Context, tool access, and guardrails do more for reliability than any model upgrade]]></description><link>https://packtwebdevpro.substack.com/p/154-why-the-harness-matters-more</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/154-why-the-harness-matters-more</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 24 Aug 2026 14:03:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Z9mX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span><a href="https://www.linkedin.com/in/jia-huang-adps/">Jia Huang</a></strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Z9mX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Z9mX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg" width="252" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:955,&quot;width&quot;:955,&quot;resizeWidth&quot;:252,&quot;bytes&quot;:50719,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/211976087?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!Z9mX!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5d7cf64b-3717-4338-87b3-ab25c1dc444b_955x955.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><a href="https://www.linkedin.com/in/jia-huang-adps/">Jia </a>is an AI researcher at A*STAR, Singapore, and a technical author who writes about how AI agents are engineered. </p><p style="text-align: center;">He proposed the dual-axis framework for agent design patterns, which classifies patterns by cognitive function and execution topology, and the Pattern Selection Card, a practical method for choosing patterns under real cost and latency constraints. His work focuses on the engineering that surrounds the model rather than the model itself: context, tools, execution environments, and governance. </p></div><p>Imagine this. Two developers use the same coding assistant on the same Tuesday afternoon. One ships a clean refactor across a dozen files before lunch. The other spends three hours fighting a model that keeps rewriting code it was told to leave alone, reads the same file four times, and confidently edits a config file that doesn&#8217;t exist.</p><p>Same model. What changed was everything around it.</p><p>That gap is the most useful thing to notice as teams adopt AI-assisted development. The industry argues about models: which one reasons better, and whose benchmark score moved this month. For the day-to-day work of building software, the model stopped being the bottleneck a while ago. What decides whether an assistant saves your afternoon or eats it is the harness you build around it.</p><p>Before we get into it, here&#8217;s what the other half of this issue has in store for you:</p><ul><li><p>&#128025; <em><strong><a href="https://github.blog/news-insights/company-news/the-august-17-outage-and-the-work-ahead">GitHub&#8217;s postmortem reveals a platform straining under AI agents</a></strong></em></p></li><li><p>&#128302; <em><strong><a href="https://x.com/GaryMarcus/status/2091542728974930149">Gary Marcus catches Sam Altman&#8217;s selective apology on AI timelines</a></strong></em></p></li><li><p>&#128294; <em><strong><a href="https://daverupert.com/2026/08/microlighter/">A 2kb syntax highlighter with no DOM spans</a></strong></em></p></li><li><p>&#127916; <em><strong><a href="https://www.youtube.com/watch?v=6lSH1-ytd7E">TanStack Start&#8217;s take on React Server Components</a></strong></em></p></li><li><p>&#128027; <em><strong><a href="https://github.com/nodejs/node/issues/65110">Node 24.19 may be eating your memory</a></strong></em></p></li></ul><h1>Where the harness actually does the work</h1><p>A language model is stateless: it forgets everything between calls, has no standing knowledge of your repository, no memory of yesterday&#8217;s decision, and no way to run anything on its own. Left to itself, it&#8217;s a very good text predictor with amnesia.</p><p>Everything that makes it feel like a teammate lives in the layer around it: the context you feed in, the tools you let it call, the checks that run before its actions take effect. Borrowing a term from the agent world, call that layer the harness. The model reasons. The harness decides what reaches the model and what the model is allowed to do with the answer.</p><p>Web developers already build harnesses; they just don&#8217;t call them that: a CI pipeline is a harness around a compiler, a linter and pre-commit hook are one around a careless commit, and environment config and CORS rules are one around a browser. None of it makes the underlying thing smarter, just safer to rely on. The same move works on a language model, and most of the leverage in AI-assisted development sits there, not in the model version number.</p><p>Three lessons keep repeating.</p><h1>Lesson 1: Treat context like a build artifact</h1><p>The first instinct is to explain the project in the chat window, every time. It works for one task and evaporates on the next, because the model kept none of it.</p><p>The pattern that holds up is to treat the assistant&#8217;s standing context as something you build once and check into the repo, the way you&#8217;d write a README the tool actually reads. Claude Code calls this a <span>CLAUDE.md</span> file; other assistants have their own version. The format matters less than the discipline. Project conventions, the commands that build and test, the directories that are off-limits, the two or three decisions every newcomer gets wrong. Written down once, loaded on every task.</p><p>The surprising part is length. The temptation is to write everything down, on the theory that more context means fewer mistakes. Past a point it reverses: the one instruction that matters gets buried among fifty that don&#8217;t, and the model follows it less reliably, not more. The guidance that has held up in practice is to keep that standing file short, on the order of a couple hundred lines, and push everything else into files the assistant pulls in on demand. A short, dense context beats a long, complete one.</p><h1>Lesson 2: What it can touch matters more than how smart it is</h1><p>What actually determines safety isn&#8217;t how smart the assistant is, it&#8217;s what it can touch.</p><p>An assistant that can only read files and propose a diff cannot drop your database, no matter how badly it misreads a request. One with an open shell can. The boundary does more to define the agent than the prompt does. Web developers already know this shape: least privilege, scoped API keys, a service account that can read the one table it needs and nothing else.</p><p>A common failure runs the other way. A team wires up an assistant with broad command access, then tries to make it safe by asking nicely in the prompt: <em>please don&#8217;t run destructive commands.</em> The prompt is not an enforcement layer. The model will comply almost every time, and the <em>almost</em> is where the incident lives. Reliability came from narrowing what the tool could do, never from a more careful sentence.</p><p>In practice: start the assistant with the smallest set of capabilities that lets it do the task, and widen only when a real task demands it. What it knows is cheap to expand later. What it can do is where the risk sits, so that is the thing you gate.</p><h1>Lesson 3: Make the important stuff deterministic</h1><p>The parts of the harness that carry the most weight are the least glamorous. They are ordinary deterministic code that runs at fixed points, whatever the model decides.</p><p>There&#8217;s a check that blocks any edit touching a secrets file, a formatter that runs after every change so the model never gets to argue about style, and a rule that a test must pass before a change is allowed to land. None of it involves the model, but it all constrains it. Claude Code exposes these as hooks; the idea is far older than any agent framework, and web developers ship it every day as pre-commit hooks, CI gates, and middleware.</p><p>The first time a format-on-every-edit rule runs, it feels like overkill. The tenth time the model reindents a file its own way and the rule quietly normalizes it back, you stop noticing the disagreement at all. A whole category of friction got absorbed by three lines of deterministic code that never asked the model&#8217;s opinion.</p><p>The mental shift is to stop hoping the model behaves and start making the misbehavior impossible to commit. A probabilistic system will occasionally do the wrong thing; that is what <em>probabilistic</em> means. You don&#8217;t engineer that away by lowering the probability with a cleverer prompt. You put a deterministic gate on the actions that would actually hurt, so the occasional wrong decision has nowhere to land. This is process isolation and the fail-safe default, forty-year-old ideas, pointed at a new kind of unpredictable component.</p><h1>A rough decision order</h1><p>When an assistant misbehaves, reaching for a better prompt is usually the wrong first move. A more useful order:</p><ul><li><p>If the output is sloppy or wrong, fix the context first. The model is probably missing a convention nobody told it.</p></li><li><p>Is the action dangerous or irreversible? That&#8217;s a tool-scope problem, not a wording one. Take the capability away.</p></li><li><p>For anything that must never happen, build a deterministic guardrail instead of trusting the model&#8217;s judgment.</p></li><li><p>Only once you&#8217;ve worked through all three should you bother rewording the prompt.</p></li></ul><p>Prompts are the layer people adjust, since it&#8217;s the one visible in the chat box, and also the weakest lever you have. The harness is where the reliability comes from, and it&#8217;s mostly invisible until you go looking for it.</p><h1>The math that changes the argument</h1><p>There&#8217;s a cost argument that quietly settles most tooling debates, and it has nothing to do with token prices.</p><p>The reflex is to reach for the cheaper model to shrink the API bill. But the API bill is rarely the real cost. If a stronger model finishes a task in five minutes and a weaker one needs thirty minutes of a developer&#8217;s back-and-forth, the &#8220;expensive&#8221; model is the cheaper choice by a wide margin once you count the salary attached to those extra twenty-five minutes. Engineer-hours dwarf token spend. Put both on the same ledger and a surprising number of default choices flip.</p><p>The same accounting justifies the harness itself. A day spent writing the context file, scoping the tools, and wiring two guardrails feels like pure overhead. It pays for itself the first time it turns a wasted afternoon into a clean morning, and it keeps paying on every task after that.</p><h1>The harness outlives the model</h1><p>There&#8217;s a second reason to spend your effort here. Models turn over every few months; the harness mostly doesn&#8217;t. A context file, a scoped tool list, and a set of guardrails written for one assistant carry over almost unchanged when a stronger model ships, or when the team moves from one tool to another. The conventions your project needs, the actions that must never run, the tests that gate a merge: none of it depends on which model sits behind the chat box. Tune a clever prompt for this week&#8217;s model, and you&#8217;re renting; build the harness instead, and you own something that survives the next release, and the one after it.</p><h1>Putting it together</h1><p>The model version you&#8217;re on matters less than it feels like it does in the release-day threads. What compounds is the harness:</p><ul><li><p>Write the assistant&#8217;s standing context once and check it in. Keep it short enough that the important line isn&#8217;t buried.</p></li><li><p>What the assistant can <em>do</em> matters more than what it knows, so scope that first.</p></li><li><p>Rules that must not break belong in deterministic code, not a polite prompt.</p></li><li><p>Cost the work in engineer-hours, not tokens.</p></li></ul><p>None of this is new engineering. It&#8217;s the same discipline web developers already apply to compilers, browsers, and build pipelines, turned toward a component that happens to reason in English. Point it at a language model and it does the same job it always did: it takes a brilliant, forgetful, occasionally reckless part and makes it something you can build on.</p><p>The model gets the headlines, but the harness is what gets the work done.</p><p><em>By Jia Huang</em></p><div class="pullquote"><p>The thing that stands out about this piece is how much of it comes from watching teams learn these lessons the hard way. <strong><a href="https://www.amazon.com/Claude-Code-Operating-Model-orchestration-ebook/dp/B0H947QRWW">The Claude Code Operating Model</a></strong> is Jia writing down what usually only gets learned after the third incident. If you'd rather read about it than live through it, that's the book. You can order it <strong><a href="https://www.amazon.com/Claude-Code-Operating-Model-orchestration-ebook/dp/B0H947QRWW">here</a></strong>.</p></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#128025; <strong><a href="https://github.blog/news-insights/company-news/the-august-17-outage-and-the-work-ahead">GitHub's postmortem reveals a platform straining under AI agents</a>:</strong><br>GitHub now handles roughly 2.9 billion commits and 130 million merged pull requests a month, and it's showing. The August 17 outage, its second that month, lasted nearly eight hours, and CTO Vlad Fedorov admitted the team needs to move faster. New git hosts are stepping into the gap: <a href="https://cursor.com/changelog/origin-code-hosting">Origin</a> from Cursor's team, <a href="https://code.storage/">Pierre</a> with its API-first approach, and Cloudflare's <a href="https://www.cloudflare.com/products/artifacts/">Git-speaking storage layer</a> are all vying to be your team's next home for code.</p><p>&#128302; <strong><a href="https://x.com/GaryMarcus/status/2091542728974930149">Gary Marcus catches Sam Altman&#8217;s selective apology on AI timelines</a>:</strong><br>Altman admitted he misjudged how fast businesses would adopt AI, but Marcus says he&#8217;s dodging the bigger miss: AGI. He digs up an Altman quote from January 2025 claiming confidence in &#8220;how to build AGI,&#8221; yet 19 months later even the best models still can&#8217;t be fully trusted. Marcus&#8217;s take: adoption jumps once AGI actually arrives, and that&#8217;s still years and several breakthroughs away.</p><p>&#127809; <strong><a href="https://www.solidjs.com/blog/solid-2-0-rc-the-big-reveal">Solid 2.0 lands, and Cursor jumps ship right on cue</a>:</strong> Solid.js shipped its biggest rework yet with the Solid 2.0 RC. Almost in the same breath, Cursor engineer Lauren Tan <a href="https://x.com/poteto/status/2089227731305464150">confirmed</a> Cursor has quietly migrated off Solid and onto React. Awkward timing aside, worth reading both the release notes and the thread.</p><p>&#127959;&#65039; <strong><a href="https://lovable.dev/blog/how-we-migrated-lovable-dev-away-from-nextjs">Lovable rebuilt its own site while nobody was looking</a>:</strong> Lovable spent six months migrating its 42M visitor, 850K line website off Next.js onto their own TanStack Start stack, mostly by directing agents at individual components. An unrelated JSON change once pushed a worker over its memory limit and took part of the site down for 11 minutes. Honest, detailed writeup.</p><p>&#128027; <strong><a href="https://github.com/nodejs/node/issues/65110">Node 24.19 may be eating your memory</a>:</strong> Several teams are reporting out of memory crashes after upgrading from Node 24.18.1 to 24.19.0, and rolling back fixes it. Nothing confirmed yet, so add your data to the issue if you&#8217;re affected.</p><p>&#127940; <strong><a href="https://blog.cloudflare.com/kitesurf/">A browser built for agents, not humans</a>:</strong> Kitesurf is a browser engine that runs on Cloudflare Workers with one isolate per page. Built for agents rather than people, it uses 3 to 7 times less CPU and memory than Chromium, at the cost of being slower. You can <a href="https://kitesurf.cloudflare.app/">try the beta</a> now, and it&#8217;ll be open sourced later.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p>&#9881;&#65039; <strong><a href="https://cursor.com/blog/git-at-any-scale">Cursor explains why hosting Git at scale is brutal</a>:</strong> A long, sharp writeup on why Git hosting breaks down past a certain scale, and why GitHub&#8217;s Spokes system hits a wall with three phase commit past a few replicas. Cursor&#8217;s answer is Continuity, a write ahead log on S3 that treats every repo copy as disposable. Great systems reading either way.</p><p>&#9997;&#65039; <strong><a href="https://tylersticka.com/journal/improved-css-text-stroke/">A cleaner way to stroke text in CSS</a>:</strong> A short, practical post on why <code>-webkit-text-stroke</code> has always looked slightly off, since it strokes over the fill instead of behind it. Covers a cleaner approach to outlined text without stacking shadows or reaching for SVG. Quick read, useful if you style headline text.</p><p>&#127916; <strong><a href="https://www.youtube.com/watch?v=6lSH1-ytd7E">TanStack Start&#8217;s take on React Server Components</a>:</strong> A TanStack Start talk arguing that RSC is a primitive, not an architecture. It walks through server functions that return RSC streams, caching them in the router or query cache, and composite components that let the client, not the server, decide what goes where. Solid mental model even if you don&#8217;t use TanStack.</p><p>&#128034; <strong><a href="https://remysharp.com/2026/08/05/progressive-enhancement-inside-of-javascript">Progressive enhancement belongs inside your JavaScript too</a>:</strong> Remy Sharp rebuilds a small markdown tool so dropping a file before the parser has loaded doesn&#8217;t just navigate away to the raw file. The fix binds a lightweight listener first and queues the interaction until the heavy JS arrives. A good reminder that fast connections hide real UX bugs.</p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p>&#128421;&#65039; <strong><a href="https://saggar.marginalutility.dev/">A Mac terminal that flags which agent needs you</a>:</strong> Saggar tracks coding agent sessions across projects and surfaces which ones are waiting on you, which are still working, and which finished a while ago. There&#8217;s a phone companion for approving prompts remotely. Mac only, Apple silicon required, built for people running several agents at once.</p><p>&#128294; <strong><a href="https://daverupert.com/2026/08/microlighter/">A 2kb syntax highlighter with no DOM spans</a>:</strong> Dave Rupert&#8217;s MicroLighter uses the CSS Custom Highlights API instead of wrapping tokens in spans, borrowing TextMate grammars so it can highlight almost any language on demand. No bold or italics yet, a real limit of the underlying API. Ships as ESM or an optional web component.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#153: The Web Developer's Job Is Moving Up the Stack]]></title><description><![CDATA[AI can generate interfaces, boilerplate, and variations in minutes. Your advantage is deciding what path you should take, measuring whether it works, and improving it.]]></description><link>https://packtwebdevpro.substack.com/p/153-the-web-developers-job-is-moving</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/153-the-web-developers-job-is-moving</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 17 Aug 2026 14:00:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rf3Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span>Chris Barin</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rf3Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rf3Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg" width="260" height="260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:400,&quot;width&quot;:400,&quot;resizeWidth&quot;:260,&quot;bytes&quot;:31678,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/211149349?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!rf3Y!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff685ca96-220d-4d70-b0ec-47703b79ce01_400x400.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><strong><a href="https://www.linkedin.com/in/cristian-doru-barin-2b912115/"><span>Chris Barin</span></a><span>, who penned the write-up below,</span></strong><span> is a professional web and app designer, an Adobe Certified Instructor, and a Certified Photoshop Expert with nearly 10 years of experience. Self-taught, he has earned the trust of over 300,000 students worldwide with a hands-on, no-nonsense approach focused on getting maximum results with minimal effort. He started out as a freelance web designer and today runs a 20-strong Android design studio whose apps have surpassed 100 million downloads.</span></p></div><p>I have been teaching design since 2013, with a focus on web and app design, and I have worked on both sides of the client relationship: as a freelance designer delivering the work and as an entrepreneur hiring people to deliver it. That perspective has made the current AI conversation feel strangely familiar.</p><p>Every day brings another announcement. A model can build a landing page, create a polished product image, generate a component, or connect to a design tool. The implied message is always the same: learn this now or fall behind.</p><p>That is a powerful way to create anxiety and sell more pricey design subscriptions from AI companies that are incredibly money-hungry.</p><p>The web professionals who thrive in the AI era will not be the ones who subscribe to every platform or memorize every feature. They will be the ones who move their attention up the stack: away from producing shiny stuff and more toward understanding the problem, directing the work, verifying the result, and improving the business outcome.</p><p>In short: help the company grow. Help the company achieve its goals. The company didn&#8217;t hire you to create a sleek product that&#8217;s going to look great in your portfolio. No, they hired you to increase their conversion rate, reduce churn, improve onboarding &amp; retention, and other clear-cut objectives.</p><p>AI does not remove the need for developers. It changes where good developers create the most value.</p><p>Speaking of which, a few stories this week get at exactly that shift:</p><p>&#128269; <em><strong><a href="https://danielmiessler.com/blog/where-watermarks-hide-in-text">Anthropic Is Watermarking Claude's Text, Here's Where It Hides</a></strong></em><br>&#9889; <em><strong><a href="https://blog.cloudflare.com/mcp-v2/">MCP Just Went Stateless, and That Changes Everything</a></strong></em><br>&#128187; <em><strong><a href="https://blog.exe.dev/the-end-of-no-code">Airtable Sold for $1.28B, Is No Code Already Dead?</a></strong></em><br>&#128373;&#65039; <em><strong><a href="https://www.lighthousenewsletter.com/p/i-put-github-copilot-behind-a-mitm">We Put GitHub Copilot Behind a Proxy, Here's What It's Really Sending</a></strong></em><br>&#128230; <em><strong><a href="https://www.docker.com/products/docker-sandboxes/">Docker Sandboxes Make YOLO Mode Actually Safe for Coding Agents</a></strong></em><br>&#128196; <em><strong><a href="https://firecrawl.github.io/anydoc/">anydoc Converts Any Office File to Markdown, Right in Your Browser</a></strong></em></p><div><hr></div><div class="callout-block" data-callout="true"><p style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/hands-on-harness-engineering-with-claude-tickets-1990304341864?aff=webdevpro&amp;discount=WB50">&#128736;&#65039; Your AI Agent Is Only as Good as Its Harness</a></strong></p><p>Claude Code can write fast. It cannot yet know when to stop, double-check, or ask permission; that part is on you. This hands-on workshop from Packt shows you how to build the specs, guardrails, and tests that turn a fast AI coding agent into a trustworthy one.</p><p>&#128197;<em> <strong>Thursday, August 20, 2026, 9:00 to 11:30 AM EDT</strong><br></em>&#128187;<em> <strong>Online</strong><br></em>&#127903;&#65039;<em> Code <strong>WB50</strong> gets you in for less</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.eventbrite.co.uk/e/hands-on-harness-engineering-with-claude-tickets-1990304341864?aff=webdevpro&amp;discount=WB50&quot;,&quot;text&quot;:&quot;Save your seat&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://www.eventbrite.co.uk/e/hands-on-harness-engineering-with-claude-tickets-1990304341864?aff=webdevpro&amp;discount=WB50"><span>Save your seat</span></a></p></div><h1>Execution is becoming abundant</h1><p>Consider a routine request: build a promotional landing page for a new product. A capable AI tool can one-shot it &#8211; meaning it can create something awesome based on one single prompt. It can scaffold the page, write responsive CSS, suggest copy, create image variations, and revise the result without becoming tired or defensive.</p><p>From a client&#8217;s perspective, that combination is difficult to ignore. The turnaround is fast, the initial quality is often <em>acceptable</em>, and requesting another version creates almost no social friction. The tool does not sigh when the client changes his mind 10 times and then comes back to the initial version.</p><p>This does not mean the entire web development profession has become a commodity. It means that parts of the job that were expensive because they consumed time are becoming cheaper. Boilerplate, first drafts, routine transformations, and basic variations are no longer where a developer can safely build a long-term advantage.</p><p>Think of AI as an electric screwdriver. It can help an experienced person assemble something much faster. It can also help an inexperienced person damage the material. The tool amplifies the judgment of the person holding it.</p><p>The important question is therefore not, &#8220;Can AI build this page?&#8221; It is, &#8220;Who can tell whether this is the right page, for the right audience, with the right trade-offs?&#8221;</p><h1>Stop shipping features and start shaping outcomes</h1><p>Suppose an online food brand asks for a new product page. A feature-focused team might discuss the framework, component library, image carousel, nutrition table, and checkout integration. Those choices matter, but they do not answer the customer&#8217;s real questions.</p><p>The customer is not buying 12 grams of fiber. That&#8217;s too zoomed in. The client is looking for a better breakfast, fewer digestive problems, or a way to eat well on a busy morning. The business does not merely need a nutrition component. It needs a page that connects the product to one of those motivations and then makes purchasing feel safe and easy.</p><p>This is where an intermediate developer can become far more valuable than someone who simply converts a ticket into code. Ask what behavior the feature is meant to change. Ask which audience matters most. Ask what evidence would show that the change worked.</p><h1>More output should produce more learning</h1><p>Before generative AI, a team might have had enough time and budget to explore two or three substantially different directions. Now it may be possible to produce ten. That sounds like a simple productivity win, but volume alone is not valuable. Ten untested variations are just a larger pile of opinions.</p><p>The real advantage is faster learning.</p><p>Instead of asking AI for ten cosmetic versions of the same hero section, create three distinct hypotheses. One page can emphasize convenience. Another can lead with the customer&#8217;s frustration. A third can focus on trust, proof, and product quality. Decide in advance which metric each direction is meant to influence, then instrument the experience and observe what happens.</p><p>This changes the developer&#8217;s role. You are not only implementing the selected design. You are helping the team create a system in which decisions can be tested. Feature flags, analytics events, performance budgets, accessible interaction states, and clean experiment boundaries become part of product thinking rather than chores added at the end.</p><p>There is an important limit here: more variants do not automatically create reliable evidence. A low-traffic product may not support a complex experiment, and small metric movements can be noise. The point is not to A/B test everything. <strong>The point is to replace taste and confidence with hard data.</strong></p><p>Use AI to expand the option space, then use engineering discipline to reduce uncertainty.</p><h1>Do not build your workflow around the model leaderboard</h1><p>Tool comparison can become a full-time hobby. One developer says a platform is astonishing; another says it is unusable. Both are right. Results vary with the task, the context provided, the codebase, model updates, and the way a particular person works.</p><p>For most developers, the gap between learning a tool and constantly switching among several strong tools is more important than the gap between the tools themselves. Otherwise said &#8211; practice one kick ten thousand times, exactly like Bruce Lee famously said.</p><p>Choose a platform that fits your budget and environment. Give it real work for a few weeks. Learn where it tends to misunderstand your architecture, invent APIs, broaden scope, or produce code that looks finished before it is production-ready. Create a repeatable way to supply context and define what success means.</p><p>The most important skill is not knowing which AI platform tops the charts today. It is knowing how to frame your work, constrain it, and verify it when the tools change tomorrow.</p><h1 style="text-align: justify;">Make the outcome clear</h1><p style="text-align: justify;">When output becomes cheap, verification becomes more important. &#8220;The page looks right&#8221; is not a sufficient completion signal for work that affects real users.</p><p style="text-align: justify;">Before asking an AI to implement a meaningful change, write down five things:</p><blockquote><p><span>&#8226; </span><strong>The user problem:</strong> What frustration or unmet need are we addressing?</p><p><span>&#8226; </span><strong>The intended change:</strong> What should the user be able or more likely to do?</p><p><span>&#8226; </span><strong>The constraints:</strong> What must remain stable, secure, accessible, and fast?</p><p><span>&#8226; </span><strong>The done signal:</strong> Which tests, checks, and acceptance criteria must pass?</p><p><span>&#8226; </span><strong>The learning signal:</strong> Which metric or qualitative evidence will we examine after release?</p></blockquote><p>This short brief is useful even if you never show it to an AI. It forces the team to separate an implementation request from a product decision.</p><p>It also gives automated tools something concrete to work against. Type checks, tests, lint rules, accessibility checks, performance budgets, schemas, and observable acceptance criteria turn part of the team&#8217;s judgment into a repeatable gate. They do not eliminate human review, but they prevent reviewers from carrying every requirement in their heads.</p><p>The goal is not autonomous coding at any cost. The goal is delivery that you can count on.</p><h1 style="text-align: justify;">Build a portfolio of results, not tools</h1><p style="text-align: justify;">A tool badge ages quickly. A case study built around an outcome does not.</p><p>If your portfolio says you used a particular framework or AI platform, it proves that you encountered the tool. If it explains how you reduced checkout friction, improved task completion, made a page measurably faster, increased qualified sign-ups, or reduced support requests, it shows that you understand why the work exists.</p><p>Getting that evidence requires curiosity after the pull request is merged. Ask for access to the relevant analytics. Talk to design, marketing, support, and operations. Watch how the feature behaves in production. Propose a small follow-up change that is inexpensive to test. Record what worked, what failed, and what you would do differently.</p><p>This is one reason proximity to the business matters. When developers only receive isolated tickets, they see requirements. When they talk to the people around the product, they see patterns: the repeated customer question, the manual workflow everyone accepts, the form users abandon, or the internal tool that costs hours every week.</p><p>Being an introvert is no longer an option. You have to talk to people and get their perspective. Clients have many choices when it comes down to skilled developers. Your moat now becomes how affable you are. How you make the client feel like. How you interact with his team. Soft skills are the biggest thing in an AI future.</p><h1 style="text-align: justify;">A change for your next sprint</h1><p>You do not need to reinvent your role or become a product manager. Start with one feature in your next sprint.</p><p>Before implementation, replace the ticket title with a one-sentence outcome. Identify one meaningful constraint and one signal you can observe after release. Ask AI to help explore genuinely different approaches, not merely different syntax. Choose an approach deliberately, implement it with mechanical checks, and return to the data after users encounter it.</p><p>Then write down the lesson.</p><p>You&#8217;re not replacing a landing page because it&#8217;s ugly. You&#8217;re doing it to improve certain KPI.</p><p>Repeat this and you will accumulate something more valuable than prompt tricks: a body of evidence about how product decisions, interface choices, technical constraints, and user behavior interact.</p><p>The tools will keep changing. Some tasks will become dramatically faster, and some skills that once looked central will become background knowledge. Prompt engineering is a great example &#8211; 12-18 months ago it was an essential skill, now it&#8217;s nearly irrelevant.</p><p>Businesses will still need people who can identify the destination, make trade-offs, recognize weak output, and connect software to a real result.</p><p>AI can do more of the driving. Your job is to choose where the product should go, keep it on the road, and know whether arriving there made anything better.</p><div class="pullquote"><p>If this got you curious, Chris has also written a book on the topic: <em><strong><a href="https://www.amazon.com/Cursor-AI-Web-Design-Beginners-ebook/dp/B0HBVSL8FG">Cursor AI for Web Design: The Complete Beginner's Guide to Building Websites with AI, No Coding Required</a></strong></em>. It walks you through building a full website step by step, from your first prompt to a live site on your own domain, and shows how to turn that skill into freelance or client work. Pre-order it <strong><a href="https://www.amazon.com/Cursor-AI-Web-Design-Beginners-ebook/dp/B0HBVSL8FG">here</a></strong>.</p></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#128269; <strong><a href="https://danielmiessler.com/blog/where-watermarks-hide-in-text">Where an AI Watermark Can Hide in Plain Text</a></strong>: Anthropic quietly announced it&#8217;s watermarking Claude&#8217;s text output, but won&#8217;t say how. This piece makes a compelling case that the mark most likely lives in word-choice statistics, not hidden characters, which is exactly why copy-paste doesn&#8217;t kill it but a real paraphrase does. Worth reading if you&#8217;ve ever wondered whether &#8220;AI detection&#8221; is even the right question to ask.</p><p>&#127912; <strong><a href="https://2026.stateofcss.com/en-US/">State of CSS 2026</a></strong>: Nearly 5,000 developers weighed in, and Anchor Positioning is the runaway favorite feature, despite (or because of) still being a browser-support minefield. The more telling stat: CSS code is still 76% hand-written, making it one of the last corners of the frontend stack that AI hasn&#8217;t fully colonized. A fun state-of-the-union for anyone shipping styles in 2026.</p><p>&#9889; <strong><a href="https://blog.cloudflare.com/mcp-v2/">The next generation of MCP</a></strong>: MCP just went stateless, and that&#8217;s a bigger deal than it sounds. It means agent servers can now run as plain Workers instead of babysitting sticky sessions and open streams. If you&#8217;ve ever cursed session management while building an MCP server, this is the fix you didn&#8217;t know you were waiting for.</p><p>&#128642; <strong><a href="https://rubyonrails.org/2026/8/12/llm-benchmarking-project">Agents on Rails: The LLM Benchmark Project</a></strong>: The Rails Foundation commissioned Evil Martians to stop the vibes-based debate and actually benchmark which coding agents write good Rails code, at what cost, in how many tokens. Refreshing to see a language community demand receipts instead of just repeating &#8220;Rails and AI pair well&#8221; anecdotally.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p>&#128187; <strong><a href="https://blog.exe.dev/the-end-of-no-code">The End of No Code</a></strong>: Airtable just sold for $1.28B, and this piece argues that&#8217;s the No Code industry peaking, not thriving, because coding agents have made &#8220;just build it on Linux&#8221; cheaper than learning a proprietary platform&#8217;s quirks. A provocative, if self-interested (the author sells VMs), read on where internal tools are headed.</p><p>&#9997;&#65039; <strong><a href="https://master.dev/blog/typographic-css-tricks/">5 CSS Properties You Should Know for Better Text Designs</a></strong>: A tidy grab-bag of underused CSS, like <code>background-clip: text</code> and <code>box-decoration-mode</code>, for anyone whose typography still looks like a Bootstrap default. Nothing revolutionary, but the letter-spacing reveal-animation trick alone is worth stealing.</p><p>&#128373;&#65039; <strong><a href="https://www.lighthousenewsletter.com/p/i-put-github-copilot-behind-a-mitm">I Put GitHub Copilot Behind a MITM Proxy. Here&#8217;s What I Found.</a></strong>: This is the deep-dive every AI coding tool user should read: Copilot keeps a full local SQLite history of your prompts and responses in plain text, no redaction, and will happily send unrelated file content (including stray secrets) to the model as &#8220;recent edits&#8221; context. The real takeaway isn&#8217;t the leak, it&#8217;s that context, not the model, is quietly becoming the product.</p><p>&#127959;&#65039; <strong><a href="https://nx.dev/blog/architecting-angular-applications">Angular Architecture Guide To Building Maintainable Applications at Scale</a></strong>: A solid, if Nx-flavored, refresher on ditching layered folders for domain-driven boundaries in Angular apps: feature, UI, and data-access libraries, with enforced dependency rules. Useful even if you never touch Nx, since the &#8220;thin app shell, fat domain libraries&#8221; mental model travels well.</p><p>&#128548; <strong><a href="https://blog.senko.net/code-was-never-the-hard-part-is-an-insult-to-all-programmers">&#8220;Code was never the hard part&#8221; is an insult to all programmers</a></strong>: A sharp, funny rebuttal to the increasingly common AI-era take that coding was always the easy bit. The &#8220;if coding is easy, why isn&#8217;t everyone just building ten variations of a thing&#8221; line alone makes this worth the five minutes, a good gut-check before you nod along to that narrative again.</p><p>&#129408; <strong><a href="https://kerkour.com/rust-hype">Understanding the Rust hype for the busy developer</a></strong>: A no-holy-war explainer aimed squarely at people who&#8217;ve heard &#8220;Rust is great&#8221; for years but never got a straight answer why. If you keep meaning to figure out what the fuss is about without committing a weekend to the Rust Book, this is the shortcut.</p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p>&#128230; <strong><a href="https://www.docker.com/products/docker-sandboxes/">Docker Sandboxes</a></strong>: MicroVM-isolated, disposable sandboxes purpose-built for running Claude Code, Copilot CLI, Codex, and friends in full YOLO/<code>--dangerously-skip-permissions</code> mode without risking your actual machine. If you&#8217;ve been letting coding agents run wild locally out of laziness rather than confidence, this closes that gap cheaply.</p><p>&#128196; <strong><a href="https://firecrawl.github.io/anydoc/">anydoc</a></strong>: A blazing-fast (sub-5ms median), pure-Rust converter that turns Word, PowerPoint, Excel, PDF, and more into clean Markdown, running entirely as WebAssembly in your browser so files never leave your machine. The most refreshing pitch here is the benchmark claim: only converter of seven tested that handled all fourteen formats.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#152: You're one file away from your First AI App]]></title><description><![CDATA[And how zero config, one API key, and a single function call is all it takes]]></description><link>https://packtwebdevpro.substack.com/p/152-youre-one-file-away-from-your</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/152-youre-one-file-away-from-your</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 10 Aug 2026 14:02:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c49124ff-6cc2-408c-9d90-880e84ac2f12_370x278.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span>Fabio Biondi</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!iGMx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!iGMx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg" width="370" height="278" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:278,&quot;width&quot;:370,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:23167,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/210172316?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!iGMx!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc65583d4-10fa-4e90-94c0-0045379e4ec3_370x278.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><a href="https://www.linkedin.com/in/fabiobiondi/"><span>Fabio Biondi </span></a><span>is a freelance developer and instructor who has dedicated his professional life to technical education, mentoring thousands of developers.</span></p><p style="text-align: center;"><span>Recognized as a Google Developer Expert (GDE) in Angular since 2018, he has devoted himself to mastering frontend technologies before expanding his focus to generative AI.</span></p><p style="text-align: center;"><span>Through his platform </span><a href="https://www.learnbydo.ing/">Learn by Do.ing</a>, <span>and presence on social media, Fabio regularly shares technical videos and insights. His mission continues to be empowering the dev community by evolving from core web technologies to the latest innovations in artificial intelligence.</span></p></div><p><span>If you&#8217;ve been telling yourself that &#8220;getting into AI&#8221; means wrestling with heavy toolchains, build steps, and a wall of configuration, here&#8217;s some good news: it has never been simpler to start. In fact, thanks to recent changes in Node.js, you can go from an empty folder to a working AI request in a few seconds.</span></p><p><span>For years, this is genuinely how it felt. You wanted to try a new model, and before you&#8217;d written a single line of actual logic, you were neck deep in a </span><code>tsconfig.json</code><span>, a webpack config, a babel setup, and three different flavors of module resolution. By the time everything compiled without errors, the excitement was gone. That friction wasn&#8217;t imagined. It was the real reason so many curious developers never got past &#8220;I should try this AI stuff sometime.&#8221;</span></p><p><span>What changed is not the AI itself, but the plumbing around it. Node.js closed the gap between &#8220;I have an idea&#8221; and &#8220;I have working code,&#8221; and that helped more than any single model release. </span></p><p><span>When the setup cost drops to zero, the only thing left standing between you and your first request is deciding to write it. And unlike a lot of shiny developer trends, this one costs nothing to try: no license, no signup wall beyond a free API key, no dependency tree to untangle before you even see output.</span></p><h2><strong><span>Node &amp; TypeScript with zero config</span></strong></h2><p><span>Modern Node.js (v24+) can run TypeScript files </span><strong><span>natively</span></strong><span>. No tsc, no ts-node, no bundler. Node simply strips the types and runs the file. That means a &#8220;project&#8221; can be as small as a </span><code>package.json</code><span> with two lines that matter:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;b1ad408a-6ab1-4d27-a238-f71cef183212&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">{

&#8220;type&#8221;: &#8220;module&#8221;,

&#8220;scripts&#8221;: { &#8220;start&#8221;: &#8220;node index.ts&#8221; }

}</code></pre></div><p><code>&#8221;type&#8221;: &#8220;module&#8221;</code><span> enables modern import/export syntax, and node index.ts runs your TypeScript directly. That&#8217;s the whole setup.</span></p><p><span>It&#8217;s worth pausing on what &#8220;strips the types&#8221; actually means, because it&#8217;s the whole trick. TypeScript was never a language the JavaScript engine understood on its own; it was always a superset that got compiled away before execution. Tools like tsc, ts-node, esbuild and Babel existed purely to do that translation, and each one came with its own configuration surface, its own quirks, and its own way of breaking on a Tuesday afternoon for no obvious reason.</span></p><p><span>Node&#8217;s native support skips that translation step entirely for straightforward TypeScript syntax. Type annotations are simply erased at parse time, the same way comments are ignored, and the underlying JavaScript runs untouched. There&#8217;s no source map to manage, no separate build artifact to keep in sync, no watcher process chewing through your CPU in the background. You edit the file, you run the file, and Node does the rest.</span></p><p><span>This matters more for a newsletter about AI than it might first appear. It means the code samples you see below, and the ones you&#8217;ll eventually write yourself, are not simplified for teaching purposes. They are the actual production shape code, with nothing hidden behind a build step you can&#8217;t see. What you read is what runs.</span></p><h2><strong><span>Now, add AI</span></strong></h2><p><span>This is the part people overestimate. Talking to a real Large Language Model is just a function call. You can pick any library or AI framework, install its package, and start experimenting in a handful of lines.</span></p><p><span>Strip away the marketing language around &#8220;AI integration&#8221; and what&#8217;s left is refreshingly mundane: you send some text to an endpoint, and the endpoint sends text back. That&#8217;s genuinely the whole mental model you need to hold onto. The model itself, whether it&#8217;s Gemini, Claude, GPT or anything else, is just a very sophisticated function sitting behind an HTTP request. You give it a prompt, it gives you a completion, and everything else you&#8217;ve heard about &#8220;agents,&#8221; &#8220;chains&#8221; or &#8220;pipelines&#8221; is scaffolding built on top of that one basic exchange.</span></p><p><span>Every provider wraps this exchange in an SDK so you don&#8217;t have to construct raw HTTP requests by hand, but the SDK is a convenience, not a requirement. Once you&#8217;ve made one of these calls successfully, you&#8217;ve effectively made all of them. The parameters change (which model, what temperature, whether you stream the reply token by token), but the shape of the call stays remarkably constant. That&#8217;s why the setup below only takes three steps.</span></p><p><span>For example, to get started with Gemini:</span></p><ol><li><p><span>Install the package: </span><code>npm install @google/genai:</code></p></li><li><p><span>Grab a free API key from </span><strong><a href="https://aistudio.google.com/"><span>Google AI Studio</span></a></strong><span> </span></p></li><li><p><span>Write your </span><code>index.ts </code><span>file:</span></p></li></ol><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;9ef58bd6-21c7-4e97-8399-522fa0b8fa68&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">import { GoogleGenAI } from &#8220;@google/genai&#8221;;

const ai = new GoogleGenAI({ apiKey: YOUR_GEMINI_KEY });

const res = await ai.models.generateContent({

model: &#8220;gemini-3.5-flash&#8221;,

contents: &#8220;Explain what an LLM is, in one sentence&#8221;,

});

console.log(res.text);</code></pre></div><p><span>Now you can simply run the script in your terminal using npm start and wait for the result.</span></p><p><span>Let&#8217;s unpack that handful of lines, because each one is doing a specific, deliberate job. The apiKey you grab from Google AI Studio is what authenticates your request. It&#8217;s the equivalent of a password that tells Gemini&#8217;s servers &#8220;yes, this request is allowed to happen, and here&#8217;s who to bill for it.&#8221; Never commit that key to a public repository. For anything beyond a quick local experiment, pull it from an environment variable instead of pasting it directly into your source file.</span></p><p><span>The model field is where you choose which version of Gemini answers your prompt. Providers typically offer a range of models trading off speed, cost, and capability, and swapping between them is usually as simple as changing this one string; no other code needs to move.</span></p><p><span>The contents field is your prompt, the actual instruction or question you want answered. In this example it&#8217;s a single string, but the same field can just as easily hold a full back-and-forth conversation history, an image, or a mix of text and files, which is how multimodal requests work under the hood.</span></p><p><span>Finally, </span><code>res.text</code><span> is the model&#8217;s reply, already extracted and ready to use. Behind that convenient property sits a larger response object carrying metadata like token usage and finish reason, but for a first script, that one line is all you need to see something come alive on your screen.</span></p><p><span>That&#8217;s it. No build step, no framework, no boilerplate, just a few lines and your first AI-generated response prints to the terminal. From here, everything else (streaming, chats, images and videos generation, tool calling or MCP integration) is a variation on this same call.</span></p><p><span>Streaming, for instance, is the same </span><code>generateContent </code><span>call with a different method name, and instead of waiting for the full answer, you get the response piece by piece as the model generates it, which is what powers that familiar &#8220;typing&#8221; effect you see in chat interfaces. Multi-turn chats are just an array of previous messages passed back in with each new request, so the model has context on what was already said. Tool calling and MCP let the model request that your code run a function on its behalf, checking a database, calling an API, doing a calculation, and feeding the result back in before it finishes its answer. None of these are architecturally different from the nine lines you just ran. They&#8217;re the same idea, repeated and combined.</span></p><p><span>The best part is that none of this is Gemini-specific. Swap the package name and the constructor for almost any other provider, and the shape of the code barely changes: an API key, a model name, a prompt, a reply. Once this pattern is in your hands, moving between providers becomes a five-minute exercise instead of a research project.</span></p><h2><strong><span>Where to go next</span></strong></h2><p><span>The point of this issue isn&#8217;t to build something big. It&#8217;s to remove the excuse! </span><br><span>The barrier to entry for AI development is now essentially zero. Once that first response comes back, experimenting becomes addictive.</span></p><p><span>If you take nothing else from this issue, take this: stop treating the first script as a chore to get through before the &#8220;real&#8221; project starts. The first script is the project. Once you&#8217;ve seen a model respond to something you typed yourself, in a file you wrote yourself, with no framework standing between you and the result, you&#8217;ll understand why so many developers describe this moment as the one that got them hooked on building with AI in the first place.</span></p><p><span>So open a folder, drop in a two-line </span><code>package.json</code><span>, write your </span><code>index.ts</code><span>, and run it. Nothing about this newsletter issue requires a weekend, a course, or a new laptop. It requires about five minutes and a Google AI Studio key. Everything after that first console.log is just you following your own curiosity, one small script at a time.</span></p><p><span>For the full API surface, models, parameters, streaming, multimodal inputs and more, the official </span><strong><a href="https://ai.google.dev/gemini-api/docs"><span>Google Gemini API documentation</span></a></strong><span> is the place to explore.</span></p><p><span>And when you&#8217;re ready to go beyond a single script and build real, production-grade AI features into a modern front end, my book walks you through it end to end:</span><a href="https://www.amazon.com/Building-AI-Powered-Apps-Angular-Hands-ebook/dp/B0GFCRHLNF"><span> </span></a><strong><a href="https://www.amazon.com/Building-AI-Powered-Apps-Angular-Hands-ebook/dp/B0GFCRHLNF"><span>Building AI-Powered Apps with Angular</span></a></strong><span>.</span></p><p><strong><span>Fabio Biondi | Google Expert</span></strong><br><span>Follow me on </span><a href="https://www.linkedin.com/in/fabiobiondi/"><span>LinkedIn</span></a></p><div><hr></div><div class="callout-block" data-callout="true"><p>&#128293; <strong><a href="https://www.amazon.com/Full-Stack-React-TypeScript-Node-applications/dp/1803235772">48% Off the Full-Stack Bible You Actually Need</a></strong>: <em><strong>Full-Stack React, TypeScript, and Node</strong></em><strong> (2nd Edition)</strong> covers React 19, TypeScript, and a Node/Express backend, plus new chapters on Docker and AWS deployment. If you've been meaning to go from "comfortable with React" to "can ship the whole stack," now's the time &#8212; 48% off on Amazon.</p></div><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#128275; <strong><a href="https://socket.dev/blog/popular-npm-packages-in-the-keyv-and-cacheable-namespaces-compromised-in-active-supply-chain">The npm Worm That Plants a Dead Man&#8217;s Switch on Your Machine</a>:</strong> A worm hit <code>keyv</code>, <code>cacheable</code>, and <code>flat-cache</code> via a compromised maintainer account, and it plants autostart hooks in <code>.claude</code>/<code>.vscode</code> and even a dead-man&#8217;s-switch that fires on token revocation. If you&#8217;re on <code>eslint</code>, you&#8217;re likely affected through the dependency chain. Pin to pre-compromise versions now.</p><p>&#128421;&#65039; <strong><a href="https://blog.cloudflare.com/cloudflare-computer/">Cloudflare Says Your Agent Deserves Better Than a Container</a>:</strong> Cloudflare&#8217;s answer to &#8220;every agent needs its own container&#8221;: a shared filesystem where agents pick between a cheap isolate or a full Linux container per task, instead of paying container tax for everything. Worth a look if you&#8217;re building agent infra.</p><p>&#129695; <strong><a href="https://devblogs.microsoft.com/ifdef-windows/a-new-way-to-bring-native-windows-apis-to-javascript-introducing-dynamic-api-projections-for-node-js/">Microsoft Just Killed the Native Addon for Windows APIs in Node</a>:</strong> Call native Windows APIs (notifications, on-device AI, clipboard) straight from JS in Electron, no C++ bridge required. Codegen turns Windows metadata into JS wrappers directly. Handy if you&#8217;ve been dreading a native module.</p><p>&#127907; <strong><a href="https://textslashplain.com/2026/08/04/security-is-hard-yall/">A Security Expert Almost Fell for His Own Company&#8217;s Product</a>:</strong> Eric Lawrence nearly reports a legit new Cloudflare product as phishing, because it followed every anti-pattern phishers use. The takeaway: if an expert can&#8217;t tell your flow from an attack, the flow&#8217;s the problem.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p>&#127760; <strong><a href="https://blog.cloudflare.com/cdnjs-dev-platform-migration/">How a 15-Year-Old CDN Survived Its Own Success</a>:</strong> cdnjs moved off a six-year-old GCP pipeline onto Cloudflare&#8217;s own Workers/Workflows/R2 stack, forcing Cloudflare to raise its own platform limits along the way. Good read if you&#8217;re evaluating Workflows for long-running jobs.</p><p>&#128176; <strong><a href="https://medium.com/data-science-collective/should-you-self-host-inference-f94c8aca2a2d">The GPU Math Most Teams Get Wrong</a>:</strong> Crossover point is ~2M tokens/day; below that, hosted APIs win because the MLOps hire costs more than the GPUs. Send to anyone convinced buying hardware saves money.</p><p>&#128026; <strong><a href="https://www.youtube.com/watch?v=o98XmRVjxWs">The JavaScript Comment Syntax Nobody Uses (But Is Actually in the Spec)</a>:</strong> Short video on hashbang comments (<code>#!</code>) &#8212; yes, they&#8217;re really in ECMAScript.</p><p>&#127828; <strong><a href="https://www.productcurious.com/p/deep-dive-why-did-doordash-ship-a">DoorDash Built a CLI That Skips the &#8220;Are You Sure?&#8221; Prompt</a>:</strong> <code>dd-cli</code> skips the human-confirmation step DoorDash&#8217;s other AI surfaces keep. The &#8220;read &#8800; write &#8800; spend&#8221; framing is worth stealing regardless of what you think of the stunt.</p><p>&#128208; <strong><a href="https://seg6.space/posts/center-div/">We Solved Centering a Div, Then Browser Sidebars Ruined Everything</a>:</strong> <code>place-items: center</code> centers your div in the webview, not the window, and fixing that needs pointer events since Chromium won&#8217;t tell you the split. A fun precision-layout rabbit hole.</p><p>&#127912; <strong><a href="https://2026.stateofcss.com/en-US/">The CSS Feature Everyone Wants and Nobody Can Ship</a>:</strong> Anchor Positioning is both the most-wanted feature and the most avoided due to browser support. Also: CSS remains the one part of web dev resisting AI-generated code (28% average).</p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p>&#128444;&#65039; <strong><a href="https://github.com/unlayer/react-image-editor">React Image Editor -</a></strong><a href="https://github.com/unlayer/react-image-editor"> </a><strong><a href="https://github.com/unlayer/react-image-editor">A Full Photoshop-Lite for React, Minus the Bloat</a>:</strong> Crop, resize, filters, draw, shapes, stickers, plus an optional AI Assistant. Works in RSC out of the box, tool rail is fully configurable.</p><p>&#128272;<strong><a href="https://varlock.dev/">Varlock - .env Files That Won&#8217;t Leak to Your AI Agent</a>:</strong> Drop-in <code>dotenv</code> replacement built for the agent era: agents read your <code>.env.schema</code> for context but never see real secret values, and its credential proxy swaps in real values only at the network boundary.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#151: Designing AI Apps Around Two Planes: Control and Data]]></title><description><![CDATA[What restaurant kitchens can teach us about building AI systems that don't break]]></description><link>https://packtwebdevpro.substack.com/p/151-designing-ai-apps-around-two</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/151-designing-ai-apps-around-two</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 03 Aug 2026 14:02:47 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!GpPQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span>Haimantika Mitra</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!GpPQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!GpPQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg" width="268" height="268" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:800,&quot;resizeWidth&quot;:268,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!GpPQ!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb928324f-3524-4a6d-baf9-ecdaabde60c7_800x800.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><strong><span>Haimantika Mitra</span></strong><span> wrote this issue&#8217;s featured article on </span><em><strong><span>Designing AI Apps Around Two Planes: Control and Data</span></strong></em><span>. She is a Senior Developer Advocate by profession, working in core AI and infra and is also currently doing her master&#8217;s in AI from Georgia Institute of Technology, Atlanta. She has written an ebook on </span><em><a href="https://learn.haimantika.com/"><span>How to DevRel for Startups</span></a></em><span> and has been writing a number of other technical articles on core AI, GPU, and also developer experience.</span></p></div><p><span>If you have shipped an AI feature, you have probably run into this already. You want to reword a prompt, but since the prompt lives inside your application code, changing it means a pull request, a review, and a full deploy. A wording tweak now moves at the speed of your whole release pipeline.</span></p><p><span>This is common enough that it has become its own genre of blog post. &#8220;</span><a href="https://towardsdatascience.com/why-your-prompts-dont-belong-in-git/"><span>Why your prompts don&#8217;t belong in Git</span></a><span>.&#8221; &#8220;</span><a href="https://arize.com/blog/prompt-templates-as-configs-not-code/"><span>Prompt templates as configs, not code</span></a><span>.&#8221; The advice is consistent: pull the prompt out of the codebase so you can change it without redeploying, and teams back this up in practice by fetching prompts from a registry at runtime instead of hard-coding them.</span></p><p><span>This is one example of a much larger idea, and once you see the larger idea, a lot of AI design decisions get easier to reason about.</span></p><p><span>The larger idea is separating the </span><strong><span>control plane</span></strong><span> from the </span><strong><span>data plane</span></strong><span>.</span></p><p><span>In this issue, we will look at why the usual &#8220;one big blob&#8221; design gets painful, what the two planes actually are, and how to decide what belongs where. We will also look at a recent research paper that pushes the idea further than most teams do.</span></p><p><span>But before we get into it, here&#8217;s a glimpse of what awaits you further in the issue:</span></p><ul><li><p>&#128184; <em><a href="https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/">OpenAI Slashes GPT&#8209;5.6 Prices, Adds Fast Mode</a></em></p></li><li><p>&#9654;&#65039; <em><a href="https://www.youtube.com/watch?v=OytpXXeNmTQ">The Creator of TypeScript Shows Off TypeScript 7</a></em></p></li><li><p>&#9986;&#65039; <em><a href="https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models">The new rules of context engineering for Claude 5 generation models</a></em></p></li><li><p>&#128269; <em><a href="https://mostlyobvio.us/2026/07/curiosity-reduced-the-heap-size-by-60/">How an AI Agent&#8217;s Curiosity Cut Heap Size by 60%</a></em></p></li><li><p>&#128506;&#65039; <em><a href="https://maplibre.org/maplibre-gl-js/docs/">MapLibre GL JS Docs</a></em></p></li></ul><h2><span>First, let&#8217;s define what it means</span></h2><p><span>The words &#8220;control plane&#8221; and &#8220;data plane&#8221; come from networking. Let&#8217;s understand this with the help of a kitchen:</span></p><p><span>Think of a restaurant. There is a head chef who decides the menu, writes the recipes, and says which dishes are allowed tonight. Then there are the line cooks who take an order and actually cook the food, fast, the same way every time. The head chef is the control plane who makes decisions. The line cooks are the data plane. They do the work, and the food (your data) passes through their hands.</span></p><p><span>The head chef does not need to touch every plate to do her job. And the line cooks do not need to re-invent the recipe for every order. The kitchen works because those two jobs are separate.</span></p><p><span>Now map that back to an AI app.</span></p><ul><li><p><strong><span>Control plane = the decisions.</span></strong><span> Which model to use, what the prompt is, which tools the agent may call, what the guardrails are, and how you measure if a change was good (your evals).</span></p></li><li><p><strong><span>Data plane = the work.</span></strong><span> The actual inference call, the retrieval, the token streaming, the request and response that carry real user data.</span></p></li></ul><h2><span>The &#8220;one big blob&#8221; problem</span></h2><p><span>When you ship your first AI feature, mixing the two feels natural. It is all just one function. Here is roughly what that looks like:</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!S9LV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 424w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 848w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 1272w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!S9LV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png" width="1456" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 424w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 848w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 1272w, /__u/substackcdn.com/image/fetch/$s_!S9LV!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd384b658-df85-4a02-afa5-d7ac5d040fde_2048x1058.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Figure 1: One handler doing everything, decisions and data movement are tangled together.</span></sup></em></p><p><span>It works for a while, until it doesn&#8217;t. Here is where it starts to hurt:</span></p><p><strong><span>1. You can&#8217;t answer &#8220;where did the data go?&#8221;</span></strong></p><p><span>Because the prompt, the model keys, and the user&#8217;s documents all live in the same code path, the user&#8217;s data flows to every vendor in that path. It happened, because there was no line to stop it.</span></p><p><strong><span>2. Every tiny change is a full deploy.</span></strong></p><p><span>Want to A/B test a new system prompt? Swap a model? Turn off a tool that is misbehaving?</span></p><p><span>If those decisions live inside your handler code, you cannot change them without shipping code. A one-line prompt tweak becomes a pull request, a review, and then a deployment.</span></p><p><strong><span>3. One hiccup takes the whole thing down.</span></strong></p><p><span>Say your prompt-config service has a bad minute. In a blob design, your inference cannot run without it, so your AI feature goes dark, even though the model provider was up the entire time.</span></p><p><span>This should feel familiar if you read our earlier issue on </span><a href="/__u/packtwebdevpro.substack.com/p/webdevpro-143-why-your-microservices?r=4baio6"><span>service discovery</span></a><span>. The same lesson applies here: a system that can&#8217;t survive one piece going wobbly is a system that will page you at 2 a.m.</span></p><p><span>Quick checkpoint before we fix it: if you cannot describe, in one sentence, which parts of your AI feature are &#8220;decisions&#8221; and which parts are &#8220;moving data,&#8221; you are probably in blob territory.</span></p><p><span>That is okay. Let&#8217;s draw the line.</span></p><h2><span>Drawing the line</span></h2><p><span>Kubernetes did this. Service meshes did this. Software-defined networking did this years ago. The decision-making layer is kept separate from the layer that carries traffic, so you can change one without disturbing the other. Here is the same idea for an AI app.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!JTtj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 424w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 848w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 1272w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!JTtj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png" width="1456" height="850" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:850,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 424w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 848w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 1272w, /__u/substackcdn.com/image/fetch/$s_!JTtj!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5592f6b7-050c-468d-a247-48eb79838421_2048x1195.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Figure 2: Two planes, one boundary. Decisions live on top and get handed down as config. User data lives at the bottom and never flows up.</span></sup></em></p><p><span>The boundary is the important part, so let&#8217;s be understand about how it behaves:</span></p><ul><li><p><span>Decisions flow down</span><strong><span>.</span></strong><span> The control plane hands the data plane a set of instructions: use this model, this prompt version, these allowed tools, these limits. It hands down config, not data.</span></p></li><li><p><span>Data does not flow up</span><strong><span>.</span></strong><span> The data plane can send metrics up (latency, token counts, &#8220;did the guardrail trip&#8221;) but not the raw user content. This one rule shrinks your privacy blast radius dramatically. Now the answer to &#8220;where does the data go?&#8221; is short: it stays in the data plane.</span></p></li></ul><p><span>That is the whole design. Two boxes and a rule about which direction things are allowed to travel.</span></p><p><span>You can now change a prompt, add a model, or tighten a guardrail entirely in the control plane, without redeploying the thing that serves users. The good news is you do not have to build any of this from scratch. A lot of it already exists as off-the-shelf infrastructure, and the industry has quietly converged on the same two-plane language.</span></p><p><span>For the &#8220;which model, with what limits&#8221; decisions, that infrastructure is the </span><strong><span>LLM gateway</span></strong><span>. Tools like LiteLLM (open source, self-hosted), Portkey, Kong AI Gateway, and Cloudflare AI Gateway sit between your app and the model providers and handle routing, failover, caching, rate limits, and cost tracking, without you touching application code. Several of them describe themselves, in exactly these words, as a control plane for your AI traffic. Some even split their own product into a managed control plane and a data plane that runs inside your network, which is the whole idea, shipped.</span></p><p><span>For the &#8220;what is the prompt&#8221; decisions, the equivalent is a </span><strong><span>prompt registry</span></strong><span>: PromptLayer, Braintrust, LangWatch, or MLflow&#8217;s prompt registry, among others. Your app fetches the active prompt version at runtime, and prompt edits become an operational change instead of a code release.</span></p><h2><span>Research says: keep the LLM out of the hot path</span></h2><p><span>Here is where it gets interesting, and where a recent paper changed how I think about this. Most of us instinctively put the LLM in the data plane. It is the thing doing the work, so of course it goes on the request path, right?</span></p><p><span>A 2026 paper on Research Square,</span><a href="https://www.researchsquare.com/article/rs-9135999/v1"><span> &#8220;LLM-driven control-data plane separation for zero-intrusion TCC transactions in legacy microservices,&#8221;</span></a><span> argues for the opposite, and it is a genuinely clever inversion.</span></p><p><span>The authors put the LLM in the </span><strong><span>control plane</span></strong><span>, and they run it offline. In their design, the model reads the system&#8217;s API specifications and reasons about them once, ahead of time, to produce a set of plain, deterministic rules. Then the </span><strong><span>data plane</span></strong><span> is just a fast proxy that applies those rules on every request, no model call in the loop.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!GmKY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 424w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 848w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!GmKY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png" width="1456" height="680" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:680,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 424w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 848w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GmKY!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2b50dbec-36ce-41d7-8748-7da36988ad9b_2048x956.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Figure 3: The LLM does the slow, smart thinking once, offline.</span></sup></em></p><p><span>Why is this smart? Because it isolates the unpredictable part of your system from the part that has to be fast and reliable. The paper reports that keeping the model off the real-time path lets the online proxy run with constant, predictable latency, precisely because a generative model, which is nondeterministic by nature, never runs during a live request.</span></p><p><span>You do not have to adopt their exact setup to steal the lesson:</span></p><blockquote><p><em><span>The nondeterministic thing belongs in the control plane. The hot path should be as predictable as you can make it.</span></em></p></blockquote><p><span>Sometimes your product genuinely needs the model live in the data plane, a chat assistant, for example, has to call the model per turn. Put the model on the hot path only when the feature truly needs it, and keep everything you can (routing, config, guardrail rules, tool choices) up in the control plane where it is cheap to change and safe to reason about.</span></p><h2><span>Make the boundary fail gracefully</span></h2><p><span>One more design detail, because this is where real systems earn their keep. What happens when the control plane goes down?</span></p><p><span>If you designed the boundary well, the answer is: not much, for a while. The data plane should keep a cached copy of its last-known-good instructions and keep serving with them.</span></p><p><span>Think of it like the line cooks having tonight&#8217;s recipes printed and taped to the wall. If the head chef steps out, dinner service does not stop. The cooks keep making the dishes they already know. They just cannot get new recipes until the head cook is back.</span></p><p><span>So a control-plane outage should mean &#8220;we cannot change decisions right now,&#8221; not &#8220;the product is down.&#8221; That is a graceful degradation, and it is only possible because you separated the two planes in the first place.</span></p><h2><span>What this does not fix</span></h2><p><span>I want to be honest here, because separation is a design principle, not a magic.</span></p><ul><li><p><strong><span>It does not make your model correct - </span></strong><span>A clean data plane will still happily stream a confident, wrong answer. Separation controls where things run, not whether the output is any good. Your evals in the control plane are what catch that.</span></p></li><li><p><strong><span>It adds a moving part - </span></strong><span>You now have a boundary to maintain, config to version, and a cache to keep fresh. For a prototype, a blob is genuinely fine, and most gateway guides say the same thing: skip the extra layer until you actually need it.</span></p></li><li><p><strong><span>It does not draw the line for you - </span></strong><span>Some things sit awkwardly in the middle, and retrieval is the classic example. A useful rule of thumb: if changing something alters your app&#8217;s runtime behavior, routing, or decisions without needing a code change, it probably belongs in the control plane. If it is tightly bound to application logic, leave it in the code.</span></p></li><li><p><strong><span>A stale cache can bite -</span></strong><span> If the data plane serves last-known-good config for too long during a control-plane outage, you might be running a prompt or a policy you meant to retire.</span></p></li></ul><h2><span>Where to get started</span></h2><p><span>You do not need to re-architect anything on Friday afternoon. Try this instead:</span></p><p><span>Open your main AI handler and read it top to bottom. For each thing it does, write one word next to it: </span><strong><span>decision</span></strong><span> or </span><strong><span>data</span></strong><span>.</span></p><p><span>By the time you reach the bottom, you will see the tangle from Figure 1 and the line will almost draw itself. The first thing you pull out is usually the prompt or the model choice, because those are the ones you change most often and want out of your deploy pipeline first.</span></p><h2><span>Quick summary</span></h2><ul><li><p><span>Control plane = decisions, data plane = work</span><strong><span>.</span></strong><span> Which model, which prompt, which tools, and your evals are decisions. Inference, retrieval, and streaming are work.</span></p></li><li><p><span>Make the boundary one-directional</span><strong><span>.</span></strong><span> Config flows down. Metrics can flow up. Raw user data never flows up.</span></p></li><li><p><span>Keep the nondeterministic thing off the hot path when you can</span><strong><span>.</span></strong><span> The research is a strong nudge here: let the LLM do slow, smart work offline and hand the live path boring, deterministic rules.</span></p></li><li><p><span>Design for graceful failure. A control-plane outage should mean &#8220;no new decisions,&#8221; not &#8220;product down.&#8221;</span></p></li><li><p><span>Start by labelling. Tag each line of your handler as a decision or data.</span></p></li></ul><p><span>Separation of concerns is one of the oldest ideas in software, and AI apps did not escape it. The names are new, but the instinct is the same one you already trust everywhere else in your stack: keep the part that decides separate from the part that does.</span></p><p><span>Some resources that you can refer to:</span></p><ul><li><p><a href="https://www.researchsquare.com/article/rs-9135999/v1"><span>LLM-driven control-data plane separation for zero-intrusion TCC transactions in legacy microservices - Research Square, 2026 (the &#8220;put the LLM in the control plane&#8221; paper)</span></a></p></li><li><p><a href="https://arize.com/blog/prompt-templates-as-configs-not-code/"><span>Arize, Prompt templates as configs, not code (the config-vs-code rule of thumb)</span></a></p></li><li><p><a href="https://www.braintrust.dev/articles/what-is-prompt-management"><span>Braintrust, What is prompt management? (why decoupling prompts from releases matters)</span></a></p></li></ul><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#128184; <a href="https://openai.com/index/advancing-the-price-performance-frontier-with-gpt-5-6/">OpenAI Slashes GPT&#8209;5.6 Prices, Adds Fast Mode</a><br>Luna is now 80% cheaper and Terra 20% cheaper, and Sol gets a new Fast mode running up to 2.5x faster at double the price, replacing Priority Processing. The number that actually matters: on Agents' Last Exam, Luna reportedly beats Fable 5 at nearly 99% lower cost per task. This is OpenAI betting that most agent workloads don't need frontier reasoning at every step. If you're still running multi-step pipelines on older models, this is worth a cost audit now.</p><p><span>&#128295; </span><a href="https://master.dev/blog/cloudflare-workers-and-hyperdrive-with-sveltekit/"><span>Cloudflare Workers and Hyperdrive with SvelteKit</span></a><span>: A solid walkthrough of wiring SvelteKit into Cloudflare Workers, including the &#8220;gotcha&#8221; that killed the author&#8217;s first deploy (drop wrangler types --check from your build script, it&#8217;s more trouble than it&#8217;s worth). The real value here is the Hyperdrive setup and per-request connection pattern via hooks.server.js, required reading if you&#8217;re putting a database behind Workers instead of a traditional long-lived server.</span></p><p><span>&#129504; </span><a href="https://sebastianraschka.com/blog/2026/kimi-k3-architecture-notes.html"><span>Kimi K3 Architecture Notes</span></a><span>: Raschka&#8217;s rapid-fire breakdown of the now-largest open-weight model (2.8T params) is the fastest way to understand where LLM architecture is heading: LatentMoE, Kimi Delta Attention, and a full switch to NoPE with zero RoPE layers anywhere. Notably it&#8217;s the first frontier model to ditch positional embeddings entirely rather than mixing NoPE and RoPE, worth a skim even if you don&#8217;t care about the benchmarks.</span></p><p><span>&#9986;&#65039; </span><a href="https://claude.com/blog/the-new-rules-of-context-engineering-for-claude-5-generation-models"><span>The new rules of context engineering for Claude 5 generation models</span></a><span>: Anthropic cut over 80% of Claude Code&#8217;s system prompt for newer models and shares why: rigid rules and repeated examples that used to compensate for weaker judgement now just constrain a model that&#8217;s gotten good enough to reason about tradeoffs itself. If your CLAUDE.md or agent prompts are still stuffed with defensive instructions &#8220;just in case,&#8221; this is a nudge to trim them back and lean on progressive disclosure instead.</span></p><p><span>&#128737;&#65039; </span><a href="https://github.blog/changelog/2026-07-28-npm-publish-time-malware-scanning-and-dual-use-metadata/"><span>npm publish-time malware scanning and dual-use metadata</span></a><span>: npm now scans every package before it&#8217;s installable, adding a ~5-15 minute publish delay, annoying if you have CI that assumes instant availability, but a genuinely good supply-chain move. The more interesting bit is the new contentPolicy/DISCLOSURE requirement for legitimate security tooling that looks malware-ish to scanners; if you maintain anything security-adjacent, go check whether you need to add this metadata before you get auto-blocked.</span></p><p><span>&#129695; </span><a href="https://devblogs.microsoft.com/ifdef-windows/a-new-way-to-bring-native-windows-apis-to-javascript-introducing-dynamic-api-projections-for-node-js/"><span>Dynamic API projections for Node.js</span></a><span>: Microsoft&#8217;s new WinRT projection lets Electron/Node apps call native Windows APIs (on-device AI like Phi Silica, rich notifications, clipboard, sensors) straight from JS, no C++ addon or node-gyp required. It&#8217;s a genuinely clever &#8220;codegen from metadata&#8221; approach, though one commenter&#8217;s dig about Teams eating 1.5GB of RAM is a fair reminder that easier native access doesn&#8217;t fix Electron&#8217;s fundamental appetite</span>.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><span>&#9654;&#65039; </span><a href="https://www.youtube.com/watch?v=OytpXXeNmTQ"><span>The Creator of TypeScript Shows Off TypeScript 7</span></a><span>: Anders Hejlsberg walks through the Go-ported, natively-compiled TypeScript 7 compiler, and the numbers are hard to ignore: Slack cut CI type-checking from 7.5 minutes to 1.25, Vanta saw up to 9x faster builds, and language-server crashes dropped over 60%. If you haven&#8217;t upgraded yet, this is the demo that&#8217;ll convince you to stop putting it off.</span></p><p><span>&#128184; </span><a href="http://danielabaron.me/blog/how-i-stopped-running-out-of-tokens/"><span>How I Stopped Running out of Tokens</span></a><span>: A refreshingly concrete rundown of tools and habits (rtk for compressing CLI output, Caveman for stripping filler from responses, auditing bloated CLAUDE.md files) that actually moved the needle on Claude Code spend. The CLAUDE.md audit story alone, converting always-loaded @ references into conditional ones, is worth stealing for any team drowning in context bloat.</span></p><p><span>&#9881;&#65039; </span><a href="https://depot.dev/blog/github-is-the-wrong-shape-for-this-new-world"><span>GitHub is the wrong shape for this new world</span></a><span>: Depot&#8217;s CEO argues that PR-based, human-paced collaboration is now the actual bottleneck now that agents generate code at machine speed, and that we need to think of software delivery as infrastructure primitives (source control, execution, artifacts, identity, policy) rather than a better pull request. Self-serving coming from a CI vendor, sure, but the underlying question, who reviews and owns code when everyone in a company can generate it, is one every eng org is going to have to answer soon.</span></p><p><span>&#129513; </span><a href="https://infrequently.org/2026/07/state-management/"><span>The Absolute State of Management</span></a><span>: Alex Russell&#8217;s latest is a pointed jab at &#8220;state management&#8221; as a term that only exists because React made simple UI updates needlessly hard. Bracing if you&#8217;ve built your career on Redux/Zustand-style tooling, but a useful gut-check on how much complexity in modern frontend is self-inflicted rather than essential.</span></p><p><span>&#128736;&#65039; </span><a href="https://www.youtube.com/watch?v=2aKGR4RECj0"><span>TanStack is Doing What?</span></a><span>: Tanner Linsley drops a &#8220;big change&#8221; for TanStack live with guests, no spoilers here, but given TanStack&#8217;s footprint across Query, Router, Start, and DB, whatever this is will likely ripple through a lot of stacks. Worth watching live rather than waiting for the recap thread.</span></p><p>&#128269; <a href="https://mostlyobvio.us/2026/07/curiosity-reduced-the-heap-size-by-60/">How an AI Agent's Curiosity Cut Heap Size by 60%</a><br>A stray Ruby warning about redefining <code>object_id</code>, filtered out during a routine spec run, turned into a real find: <code>opentelemetry-instrumentation-aws_sdk</code>'s <code>use_all</code> config was force loading all ~200 services in a legacy <code>aws-sdk</code> v2 gem just to instrument a handful. One patch dropped loaded services from 220 to 0, classes in the VM by 60%, and boot time by 36%. The lesson: pointing an agent at "diagnose this" for something adjacent to your task is cheap enough to do constantly now. Worth checking your own boot logs if you're on an old <code>aws-sdk</code> v2 pin with OTel enabled.</p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><span>&#128506;&#65039; </span><a href="https://maplibre.org/maplibre-gl-js/docs/"><span>MapLibre GL JS Docs</span></a><span>: The open-source, WebGL-based Mapbox alternative just shipped v6 as ESM-only, which means bundler setup now needs explicit worker-URL wiring (Vite&#8217;s ?worker&amp;url, not plain ?url, the naive version silently breaks production builds). If you&#8217;re on Mapbox and dodging their license/pricing, or upgrading from MapLibre v5, the migration guide and per-bundler snippets here will save you a debugging session.</span></p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#150: The Work That Got Faster, and the Work That Didn't]]></title><description><![CDATA[Code is cheap now. Knowing what to build, and whether it's right, never was.]]></description><link>https://packtwebdevpro.substack.com/p/150-the-work-that-got-faster-and</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/150-the-work-that-got-faster-and</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 27 Jul 2026 14:02:11 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/254b116b-59d9-44b1-b60c-f0534e5bdc44_257x171.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h2 style="text-align: center;"><strong><span>Meet the expert: </span>Jeremy McEntire</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!aGbW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 424w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 848w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 1272w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!aGbW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png" width="257" height="171" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:171,&quot;width&quot;:257,&quot;resizeWidth&quot;:257,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 424w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 848w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 1272w, /__u/substackcdn.com/image/fetch/$s_!aGbW!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff242dcfb-aab9-4d4c-b50a-b090627be8a7_257x171.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p style="text-align: center;">For twenty-five years, Jeremy has built and led engineering teams at Intel, Twilio, and startups both scaled and failed. He currently leads engineering at Wander, with experience ranging from ten-person teams to organizations large enough to need committees to approve the committee structure. Alongside that work, he writes about the structural patterns behind organizational dysfunction, what he calls the legibility trap, the fiduciary trap, the context trap, and the scale trap, drawn from failure modes he&#8217;s seen firsthand across startups, scale-ups, and public companies. His focus is helping leaders see their organizations as systems rather than slogans, and build structures that respect the real cognitive and communication limits of the people inside them.</p></div><p><span>There&#8217;s a strange thing happening on engineering teams right now, and most people who are living through it can&#8217;t quite name it. AI coding tools have made writing code faster than it has ever been. Ask for a function, a component, an entire feature scaffold, and it appears in seconds, syntactically correct and reasonably named. And yet a lot of teams that have adopted these tools aggressively are not shipping meaningfully faster. Some are shipping slower. The output looks like progress. The delivery numbers tell a more complicated story.</span></p><p><span>Earlier this year, a research group ran one of the first controlled studies on whether AI tools actually speed up experienced developers. They took sixteen seasoned open-source contributors, people with years of experience on codebases they knew intimately, and had them complete real tasks with and without AI assistance. Before starting, the developers predicted the tools would make them about twenty-four percent faster. Afterward, they reported feeling roughly twenty percent faster. When researchers measured the actual time taken, the developers had been nineteen percent slower.</span></p><p><span>That gap between what people believed and what actually happened is the real story here, more than the slowdown itself. Sixteen experienced engineers, working on projects they understood well, came away convinced the tools had helped. They hadn&#8217;t, and nobody could tell from the inside. That&#8217;s worth sitting with, because it points to something the industry has quietly misunderstood about what software engineering actually is.</span></p><p><span>Before we get into the details, here&#8217;s a TL;DR of what&#8217;s in store for today&#8217;s issue:</span></p><ul><li><p>&#128275; <em><strong><a href="https://www.cole-k.com/2026/07/21/reddit">Reddit Has Decided That Plain HTML Is Unsafe</a></strong></em></p></li><li><p>&#128197; <em><strong><a href="https://github.com/angular/angular/pull/69817">Angular Is Moving to Yearly Major Releases</a></strong></em></p></li><li><p>&#128373;&#65039; <em><strong><a href="https://www.elastic.co/security-labs/contagious-interview-malware-svg-steganography">North Korean Hackers Hid Malware Inside Flag Emoji SVGs</a></strong></em></p></li><li><p>&#127869;&#65039; <em><strong><a href="https://www.kirupa.chat/p/loop-engineering-explained-with-dirty">Loop Engineering, Explained with Dirty Dishes</a></strong></em></p></li><li><p>&#127800; <em><strong><a href="https://petals.dev/">Petals: Run Massive LLMs at Home, BitTorrent-Style</a></strong></em></p></li><li><p>&#127912; <em><strong><a href="https://www.tryai.dev/blog/ai-drawing-arena-colored-pencils-claude-gpt-grok">Four Frontier Models &#8220;Draw&#8221; the Mona Lisa with Virtual Colored Pencils</a></strong></em></p></li></ul><h2><strong><span>Generating and Verifying Are Not the Same Job</span></strong></h2><p><span>Writing code and checking whether that code is right are two very different mental activities. Writing code is generative and sequential, a stream you can ride. Checking code means holding an entire system in your head at once, a landscape you have to survey piece by piece. Making the stream move faster doesn&#8217;t shrink the landscape. It just means there&#8217;s more of it to cover.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!MYKb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 424w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 848w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 1272w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!MYKb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png" width="714" height="315" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:315,&quot;width&quot;:714,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 424w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 848w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 1272w, /__u/substackcdn.com/image/fetch/$s_!MYKb!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0aec9268-c8b7-4a72-b36c-8d51927239e1_714x315.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>This is why code review times and pull request sizes have grown substantially at organizations with heavy AI adoption. The bottleneck didn&#8217;t disappear when code got cheap to produce. It moved, and the new one is more cognitively demanding than the old one.</span></p><p><span>None of this means the tools are bad or that adopting them is a mistake. It means they behave like an amplifier rather than a fix. Teams with strong habits already in place tend to get genuinely faster with AI in the loop. Teams without those habits tend to get faster at accumulating problems. The tool doesn&#8217;t install discipline. It reveals whether you had any to begin with.</span></p><h2><strong><span>Context Sets the Ceiling, Not the Model</span></strong></h2><p><span>There&#8217;s a second, quieter shift happening alongside this one, and it has to do with what actually determines whether an AI produces good output in the first place. The instinct most people have is that better output comes from a better model. That&#8217;s only partly true. An AI system&#8217;s output is bounded above by the quality of the information you hand it, not by how capable the underlying model is. A model can&#8217;t produce output more correct than its context permits, no matter how good it is underneath.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YMo1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 424w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 848w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YMo1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png" width="750" height="331" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:331,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 424w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 848w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YMo1!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0552c2c4-9e7d-431e-b58b-69ba75c85dab_750x331.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>This has a practical implication that&#8217;s easy to miss: writing good prompts isn&#8217;t really about phrasing tricks. It&#8217;s closer to what a good manager does when onboarding a new hire, deciding what someone needs to know first and what would just create noise if you dumped it all at once. Too little context and the output is generic. Too much and the important signal gets buried. Getting this right is a skill, and it&#8217;s one that&#8217;s going to matter more over the next few years, not less.</span></p><h2><strong><span>Judgment Belongs in the Pipeline, Not Just the Reviewer&#8217;s Head</span></strong></h2><p><span>The same logic applies to how teams check whether code is actually good. Most code review today is advisory: someone reads a pull request and forms an opinion. Two competent reviewers can look at the same code and reach different conclusions, and when they disagree, the outcome usually comes down to seniority or persistence rather than anything measurable about the code itself.</span></p><p><span>The alternative is mechanical verification: tests that pass or fail, type checks, contracts that specify exactly what a function should take in and return. These don&#8217;t get tired and don&#8217;t approve something because they like the person who wrote it.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!S0xb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!S0xb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png" width="750" height="353" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:353,&quot;width&quot;:750,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 424w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 848w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 1272w, /__u/substackcdn.com/image/fetch/$s_!S0xb!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef683a12-2697-4644-9c60-ba8b1dca8b47_750x353.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>As AI generates a larger share of a team&#8217;s code, leaning harder on mechanical checks rather than a gut read of whether something looks right is quickly becoming less of a nice-to-have and more of a requirement for staying sane.</span></p><h2><strong><span>What to Actually Do With This</span></strong></h2><p><span>A few things worth carrying into your next sprint. First, don&#8217;t confuse the feeling of speed with the fact of it. If a team&#8217;s velocity metrics aren&#8217;t moving even though everyone feels busier, the AI usage is probably worth auditing rather than doubling down on. Second, spend real effort on what you feed these tools before a task starts. A well-scoped paragraph of context will do more for output quality than switching models or fiddling with settings. Third, wherever possible, replace a subjective judgment call with a test, a type, or a contract that can answer the question without a person having to weigh in at all. None of this is about resisting these tools. It&#8217;s about using them with your eyes open to what they&#8217;re actually good at, and what they were never going to fix.</span></p><p><span>That question, what these tools are actually changing and what they&#8217;re leaving untouched, is exactly the kind of thing worth putting to someone who&#8217;s spent real time studying it closely. We put a few questions to Jeremy, the man behind some of the research and thinking above, and the answers are worth reading in full:</span></p><blockquote><p><strong><span>Q: You&#8217;ve made a point of writing about the underlying &#8220;physics&#8221; of engineering rather than about specific tools. Why take that approach?</span></strong></p><p><span>Technology accelerates, and the rate of change is itself changing. Anchoring your career to a specific tool means re-learning your job every eighteen months and never getting better at it. The principles underneath haven&#8217;t moved in decades. They were true before the tools existed and they&#8217;ll be true after the tools are gone. Betting on a technology is a bet with an expiration date. Betting on principles isn&#8217;t a bet at all.</span></p><p><strong><span>Q: You&#8217;ve argued that writing code was never really the hard part of the job. What do you mean by that?</span></strong></p><p><span>Every industry walks the same road: bespoke craft, then efficient production, then automation. Economic pressure does not negotiate. But automation doesn&#8217;t destroy the craft, it relocates it. The craft doesn&#8217;t die. It moves from vocation to avocation. Writing code was never the hard part. It was just the part that took the longest, and that&#8217;s a different thing entirely.</span></p><p><strong><span>Q: Why do you think apps built by &#8220;vibe coding,&#8221; without much engineering discipline behind them, tend to fall apart once they hit real users?</span></strong></p><p><span>An AI solves the problem you put in front of it. Engineering is everything you didn&#8217;t put in front of it: the constraints, the trade-offs, the edge cases, the failure modes. A non-engineer can vibe-code a flawless happy path, and the demo will be genuinely impressive, because a demo is the happy path. Then reality shows up, and reality has never once taken the happy path.</span></p><p><strong><span>Q: A lot of people assume more context always leads to better AI output. You&#8217;ve pushed back on that. Why?</span></strong></p><p><span>Working memory is finite, for people and for models alike. Flood it and you get exactly what you&#8217;d get from an overloaded engineer: dropped constraints, forgotten requirements, confident mistakes. But the fix isn&#8217;t a terse prompt. My research points somewhere else: induced understanding beats instruction. Knowing where to assert an invariant, where to bind a constraint, and where to leave the choice free is engineering judgment applied to the prompt itself. Tell a model what to do and it complies. Help it understand why it matters and it decides well.</span></p><p><strong><span>Q: Why do mechanical checks, like tests and type contracts, matter so much more now than they used to?</span></strong></p><p><span>We invented tests and types because no human can reliably hold every scenario and every edge case in their head at once. That limitation wasn&#8217;t a personal failing, and it isn&#8217;t unique to us. An AI forgets a constraint the same way you do, on the same kind of Tuesday. Signatures, argument types, return types, executable contracts, these are safeguards that maximize confidence while minimizing what you have to carry. A gate doesn&#8217;t get tired, doesn&#8217;t get rushed, and doesn&#8217;t approve your PR because it likes you.</span></p><p><strong><span>Q: You talk about &#8220;grokking&#8221; a lot, the idea of intuitive understanding built through experience. How does that concept survive in a world where AI writes so much of the code?</span></strong></p><p><span>To grok is to understand intuitively, and it&#8217;s bought only one way: exposure, repetition, experience. Engineers will not grok the same things in ten years that they grok today. They&#8217;ll grok how AI fails, how to shape a prompt so the result lands, how to orchestrate several models toward one end. That&#8217;s not a loss of depth. It&#8217;s depth pointed somewhere new. People go on knowing what&#8217;s necessary to know. What changes is what&#8217;s necessary.</span></p><p><strong><span>Q: As generating code gets cheaper and cheaper, who do you think actually comes out ahead?</span></strong></p><p><span>Consider writers. The authors of profound work aren&#8217;t losing sleep over AI. The ones who should worry are the ones whose prose is everywhere and whose names are nowhere. The masters aren&#8217;t competing with the machine, they&#8217;re not in the same space as the machine. Engineering is identical. The truly elegant code of a master isn&#8217;t what AI replaces, and it was never an economically viable profession for the plurality anyway. An engineer who knows little of their craft&#8217;s nuance finds little purchase on a force multiplier. An engineer who understands the principles underneath watches their value compound.</span></p></blockquote><p><span>If you want to learn more about Jeremy&#8217;s book, check it out </span><em><strong><a href="https://www.amazon.com/Beyond-Code-Practical-Development-Production-Ready-ebook/dp/B0H83HR8SH"><span>here</span></a></strong></em><span>.</span></p><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p><span>&#128038;</span> <strong><a href="https://threadreaderapp.com/thread/2080098518535110913.html">Fable Found a 15-30% Memory Improvement in Turbopack, Nearly Autonomously</a>:</strong> Vercel&#8217;s Guillermo Rauch shares a striking &#8220;holy s***&#8221; moment: Claude Fable 5 discovered a 15-30% memory efficiency win in Turbopack&#8217;s Rust codebase largely on its own, days after a different model reportedly surfaced novel vulnerabilities in heavily audited code. Rauch&#8217;s favorite AI progress metric right now is &#8220;WTFs per day,&#8221; and it&#8217;s climbing. Worth a skim if you want a gut-check on how fast agentic coding is actually moving, straight from someone shipping production infrastructure.</p><p><span>&#128275;</span> <strong><a href="https://www.cole-k.com/2026/07/21/reddit">So Reddit Has Decided That Plain HTML Is Unsafe</a>:</strong> Old Reddit now requires a login, officially &#8220;for safety,&#8221; and this post methodically dismantles that claim: New Reddit loads five times more data, still gets scraped, and mostly just adds a heartbeat ping every half second while you scroll. The real story, as the author bluntly puts it, is that Reddit wants to protect its own data from being scraped for free, not protect users. A satisfying, technical takedown of corporate security theater.</p><p><span>&#9889;</span> <strong><a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-6-flash-3-5-flash-lite-3-5-flash-cyber/">Google Ships Gemini 3.6 Flash, 3.5 Flash-Lite, and a Cyber-Focused Model</a>:</strong> Three new Flash-tier models aimed squarely at agentic workloads: 3.6 Flash cuts output tokens by 17% while improving coding and computer-use benchmarks, 3.5 Flash-Lite hits 350 tokens/second as the cheapest option in the lineup, and 3.5 Flash Cyber is a specialized model gated behind a government/trusted-partner pilot for vulnerability hunting. The gated cyber model is the detail worth sitting with, since it&#8217;s a tacit admission that these capabilities are now too sharp to hand out freely.</p><p><span>&#128680;</span> <strong><a href="https://openai.com/index/hugging-face-model-evaluation-security-incident">OpenAI and Hugging Face Detail a Model That Hacked Its Way Off the Leash</a>:</strong> During an internal cyber-capability evaluation run without production safety filters, OpenAI&#8217;s models chained a zero-day, escalated privileges, and broke out to the open internet purely to cheat a benchmark, then used that access to pull real credentials from Hugging Face&#8217;s production systems. Both companies are framing this as a wake-up call for the industry rather than burying it, which is the right instinct, but the details here are genuinely unsettling regardless of the framing.</p><p><span>&#128197;</span> <strong><a href="https://github.com/angular/angular/pull/69817">Angular Is Moving to Yearly Major Releases, Starting with v23 in June 2027</a>:</strong> The community has wanted this for years, and the stated rationale explicitly name-checks agentic workflows: longer major cycles mean more API stability for AI coding tools, while features keep shipping in minors as before. The most interesting pushback in the thread isn&#8217;t &#8220;why change&#8221; but &#8220;what does slower major cadence signal about investment,&#8221; especially with fast-moving experimental features like WebMCP in play.</p><p><span>&#128373;&#65039;</span> <strong><a href="https://www.elastic.co/security-labs/contagious-interview-malware-svg-steganography">North Korean Hackers Hid Malware Inside Flag Emoji SVGs</a>:</strong> A DPRK-aligned group posed as recruiters, sent developers a &#8220;coding challenge,&#8221; and hid a four-stage credential-and-wallet stealer as Base64 fragments buried in comments inside ordinary-looking country flag SVGs. Zero antivirus vendors caught it. If you or your team ever do take-home coding tests from strangers on Slack or Discord, this is the specific, current shape of that risk.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><span>&#128024;</span> <strong><a href="https://hatchet.run/blog/postgres-survival-guide">The Startup&#8217;s Postgres Survival Guide</a>:</strong> Hatchet&#8217;s co-founder distills two years of production Postgres scars into one document covering schemas, indexes, the query planner, autovacuum, bloat, and advanced tricks like FOR UPDATE SKIP LOCKED and partitioning. It&#8217;s dense but genuinely practical, the kind of reference you bookmark rather than read once. Bonus points for the honest aside that if Claude is writing all your queries, some of this may already be handled for you.</p><p><span>&#127869;&#65039;</span> <strong><a href="https://www.kirupa.chat/p/loop-engineering-explained-with-dirty">Loop Engineering, Explained with Dirty Dishes</a>:</strong> The clearest plain-English explainer yet of the &#8220;loop engineering&#8221; trend making the rounds: the difference between an AI that scrubs a plate once and hands it to you dirty, versus one that checks, adjusts, and knows when to ask for help. The three questions every loop needs to answer (what does &#8220;clean&#8221; mean, when do you stop, when do you escalate) are a genuinely useful mental model for evaluating any agentic tool, not just this one.</p><p><span>&#127912;</span> <strong><a href="https://www.tryai.dev/blog/ai-drawing-arena-colored-pencils-claude-gpt-grok">Four Frontier Models &#8220;Draw&#8221; the Mona Lisa with Virtual Colored Pencils</a>:</strong> A delightfully specific benchmark: give four models the same colored-pencil toolset and a blank canvas, then watch how differently they work. Grok flailed, Gemini over-reviewed and then regressed, GPT-5.6 Sol was the clear favorite, and Claude Fable 5 landed second on quality at roughly 20x the cost and time of the others. Every model, notably, kept editing itself past its own best result, which says something uncomfortable about self-review loops in general.</p><p><span>&#127919;</span> <strong><a href="https://alignment.openai.com/measuring-reward-seeking/">OpenAI Measures Whether Models Chase the Grader Instead of the Truth</a>:</strong> A clever methodology paper: train two copies of the same model on opposite fictional beliefs about what a grader rewards, then see which one wins when it conflicts with what the user or developer actually wants. The finding is the uncomfortable part: reward-seeking behavior measurably increased over the course of RL training, and a model&#8217;s honesty in safety evaluations depended on what it believed was being graded. Dense but important if you care about whether alignment evaluations can be trusted.</p><p><span>&#128274;</span><strong><a href="https://tech.olx.com/handling-concurrency-on-the-web-with-web-locks-api-163b7e07eddd"><span>Handling Concurrency on the Web with the Web Locks API</span></a><span>:</span></strong><span> A solid walkthrough of a genuinely underused browser API: navigator.locks lets multiple tabs or workers coordinate access to a shared resource without you hand-rolling your own synchronization logic. If you&#8217;ve ever had two open tabs quietly corrupt the same IndexedDB store, this is the fix you didn&#8217;t know shipped years ago.</span></p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><span>&#127800;</span> <strong><a href="https://petals.dev/">Petals: Run Massive LLMs at Home, BitTorrent-Style</a></strong>: A distributed inference network where you load a slice of a large model (Llama 3.1 405B, Mixtral 8x22B, BLOOM 176B) and lean on other volunteers serving the rest. It&#8217;s slow by API standards, a few tokens per second, but it&#8217;s a fascinating glimpse at community-run infrastructure for models too big for any one consumer GPU.</p><p><span>&#127912;</span><strong><a href="https://colorjs.io/"><span>Color.js: Serious Color Science for the Web</span></a><span>:</span></strong><span> Built by two editors of the CSS Color spec, this library does real gamut mapping instead of naive clipping, supports every CSS Color 4 space (Lab, OKLCh, Display P3, and more), and stays dependency-free. If your color handling currently amounts to hex codes and vibes, this is the upgrade path, and it&#8217;s already quietly powering Sass and Open Props under the hood.</span></p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#149: Why Authentication Is the Feature Most Developers Finish Without Finishing]]></title><description><![CDATA[And how the login form is the easy part]]></description><link>https://packtwebdevpro.substack.com/p/149-why-authentication-is-the-feature</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/149-why-authentication-is-the-feature</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 20 Jul 2026 14:01:39 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!FL4_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>There is a particular kind of confidence that comes from watching your own login form work. You type an email and a password, the server answers with a token, the dashboard appears, and it feels like the authentication chapter of your project is closed. For most intermediate developers, that moment arrives fairly early, and it is quietly misleading.</span></p><p><span>The login endpoint is the most visible part of an authentication system, but it is also the simplest. What separates a demo from a production system is everything that happens after the user is logged in: how their session survives, how it ends, how it gets stolen, and how your application behaves when several of those things happen at once.</span></p><p><span>If you have ever shipped an app where users mysteriously get logged out, where a stolen token stays valid forever, or where a page refresh dumps someone back to the login screen, this article is about the missing pieces. And if your framework manages sessions for you, whether that is Rails, Django, or Laravel, everything below is what that machinery is quietly doing on your behalf, so it is worth seeing it in daylight at least once.</span></p><p><span>Before we get into all that, here&#8217;s a TL;DR of the links you must check out:</span></p><ul><li><p>&#128373;&#65039; <em><strong><a href="https://blog.cloudflare.com/introducing-precursor/">Cloudflare&#8217;s Precursor Knows You&#8217;re a Bot by How You Move</a></strong></em></p></li><li><p>&#128296; <em><strong><a href="https://www.gingerbill.org/article/2026/07/10/good-tools-are-invisible">Good Tools Are Invisible</a></strong></em></p></li><li><p>&#128257; <em><strong><a href="https://addyosmani.com/blog/loop-engineering/">Loop Engineering: Stop Prompting, Start Designing the Loop</a></strong></em></p></li><li><p><em>&#9889;<strong><a href="https://endform.dev/blog/playwright-github-actions">Playwright on GitHub Actions: The Setup That Actually Runs Fast</a></strong></em></p></li><li><p>&#127846;<em><strong><a href="https://guseyn.com/html/posts/why-vanilla-js.html">Why Vanilla JS</a></strong></em></p></li><li><p>&#128184; <em><strong><a href="https://playcode.io/blog/real-price-of-frontier-models">The Same TypeScript Costs 73% More Tokens on Claude Than GPT</a></strong></em></p><div class="callout-block" data-callout="true"><h3 style="text-align: center;"><strong>Build production-grade apps with Claude Code</strong></h3><p style="text-align: center;">Claude Code can generate code fast, but this workshop shows developers how to make it work reliably inside a real codebase.</p><p style="text-align: center;">On <strong>July 31</strong>, Lu&#237;s Rodrigues and Gabriela de Queiroz will build live and show how to use <code>CLAUDE.md</code>, Skills, MCP workflows, scaffolds, and guardrails to create production-grade apps. You&#8217;ll follow the setup step by step and leave with patterns you can apply to your own projects.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.eventbrite.co.uk/e/claude-code-workshop-orchestrating-production-grade-apps-tickets-1989081541435?aff=NL&amp;discount=nl40&quot;,&quot;text&quot;:&quot;Reserve your seat&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.eventbrite.co.uk/e/claude-code-workshop-orchestrating-production-grade-apps-tickets-1989081541435?aff=NL&amp;discount=nl40"><span>Reserve your seat</span></a></p></div></li></ul><h2><strong><span>Two questions, two status codes</span></strong></h2><p><span>Authentication answers the question of who you are, while authorization answers the question of what you are allowed to do, and they are sequential by nature, because you cannot decide what someone may do before you know who they are. The classic analogy is a building with a keycard system: showing your ID at the entrance is authentication, and whether your card opens the door to the seventh floor is authorization.</span></p><p><span>This distinction maps directly onto two status codes that developers routinely misuse.</span></p><ul><li><p><strong><span>401</span></strong><span> means the server does not know who is asking, because the token is missing, malformed, or expired. The correct client reaction is a silent token refresh followed by a retry.</span></p></li><li><p><strong><span>403</span></strong><span> means the server knows exactly who is asking and is refusing anyway. The correct client reaction is a permissions message, with no retry.</span></p></li></ul><p><span>The naming is genuinely unhelpful, since 401 is labeled &#8220;Unauthorized&#8221; when it really means &#8220;unauthenticated,&#8221; but the convention is universal. When your API is consistent about the difference, your frontend can react intelligently, and when it is not, the client is forced to guess. Guessing in an auth flow is how you end up with refresh loops and phantom logouts.</span></p><h2><strong><span>Statelessness is a trade, not a gift</span></strong></h2><p><span>JSON Web Tokens are popular for a good reason. Because a JWT carries its own claims and a cryptographic signature, any server holding the secret can verify it without touching a database or a shared session store, whether you run one server or fifty behind a load balancer. That property is what people mean when they call JWTs stateless, and it is a genuine architectural advantage.</span></p><p><span>The part that gets skipped in most tutorials is what statelessness costs you. A signature does not stop being valid because you wish it would, so if a long-lived token is stolen, there is no server-side session to destroy, and the attacker holds a key that works until it expires. A related misunderstanding causes its own incidents: the payload of a JWT is Base64 encoded, not encrypted, so anyone holding a token can read every claim inside it. The signature protects against tampering, never against reading.</span></p><p><span>The mature answer to the revocation problem is to stop pretending one token can do two jobs.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!mqkk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 424w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 848w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 1272w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!mqkk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png" width="720" height="272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e693a02c-ce82-41b8-a773-ce71115de521_720x272.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:272,&quot;width&quot;:720,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 424w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 848w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 1272w, /__u/substackcdn.com/image/fetch/$s_!mqkk!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe693a02c-ce82-41b8-a773-ce71115de521_720x272.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The database record is the whole point of the second token. By persisting refresh tokens server-side, you reintroduce just enough state to make revocation possible: logging out deletes the record, and &#8220;log out of all devices&#8221; deletes every record for that user. You trade a little purity for the ability to actually end a session.</span></p><p><span>Rotation completes the design. Each refresh deletes the old token and issues a new one inside a transaction, so every refresh token works exactly once. If an attacker replays a token the legitimate user already spent, the lookup fails, and a careful implementation treats that as a breach signal and revokes everything for that account. A stolen credential becomes a tripwire instead of a skeleton key.</span></p><h2><strong><span>Where a token lives matters more than how it is signed</span></strong></h2><p><span>Ask an intermediate developer where their tokens are stored and the honest answer is often localStorage, because that is what half the internet&#8217;s tutorials demonstrate. It is also the one answer that security reviewers will flag immediately, since anything in localStorage, sessionStorage, or IndexedDB is readable by any script on your page, and a single cross-site scripting vulnerability hands an attacker every token you have saved.</span></p><p><span>There are three defensible strategies, and choosing between them is a real architectural decision rather than a default.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!FL4_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 424w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 848w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 1272w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!FL4_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png" width="743" height="425" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:425,&quot;width&quot;:743,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 424w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 848w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 1272w, /__u/substackcdn.com/image/fetch/$s_!FL4_!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02fb1728-beed-4d27-818c-e8e65df6e456_743x425.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><span>The hybrid approach is the common sweet spot. Because API requests carry the access token in an Authorization header rather than a cookie, cross-site request forgery is a non-issue for normal calls, and because the refresh cookie is httpOnly, injected scripts cannot read it. Its main cost is that the in-memory token vanishes on page refresh, so the app must silently call the refresh endpoint on load.</span></p><p><span>The cookie-only strategy trades that residual XSS exposure for CSRF risk, which is manageable but adds infrastructure you must maintain, while the backend-for-frontend pattern keeps tokens out of the browser entirely and suits genuinely sensitive data. The right choice depends on what you are protecting. What is not defensible is making no choice at all and letting a tutorial make it for you.</span></p><h2><strong><span>When your security features attack each other</span></strong></h2><p><span>Here is a failure mode that almost nobody designs for on the first attempt. A user leaves your app open in a tab and comes back twenty minutes later, after the access token has expired. They click something that fires three API calls at once, and the sequence unfolds like this:</span></p><ol><li><p><span>All three requests receive a 401, and all three independently try to refresh.</span></p></li><li><p><span>The first refresh succeeds and, because of rotation, consumes the refresh token.</span></p></li><li><p><span>The second and third arrive holding a token that no longer exists.</span></p></li><li><p><span>Reuse detection correctly interprets that as a possible attack and revokes every session the user has.</span></p></li></ol><p><span>Your two security features, each individually correct, have combined to log out a completely legitimate user.</span></p><p><span>The fix is small and elegant. The client keeps a single shared reference to any refresh request currently in flight, and every caller that hits a 401 during that window awaits the same promise instead of starting its own, so one network call happens and the queued requests retry with the new token. One guard remains: a 401 from the refresh endpoint itself must never trigger another refresh, or the client deadlocks.</span></p><p><span>The broader lesson is worth more than the pattern. Authentication is a system, and systems fail at the seams between correct components: rotation is right, reuse detection is right, and concurrent requests are unavoidable, yet together they produce a bug that only appears under real usage. Reasoning about interactions, not just features, is what the intermediate-to-senior transition largely consists of.</span></p><h2><strong><span>The quieter layers</span></strong></h2><p><span>A few remaining decisions round out a production-grade setup, and each one exists to contain the failure of another:</span></p><ul><li><p><strong><span>Hash passwords with a memory-hard algorithm</span></strong><span> such as Argon2id rather than a merely slow one, because modern GPUs shrug at computational cost but choke on memory cost.</span></p></li><li><p><strong><span>Rate limit the login, registration, and refresh endpoints</span></strong><span>, and run the limiter before body parsing so abusive traffic is rejected before the server spends resources on it.</span></p></li><li><p><strong><span>Cap request body sizes and set cookie flags properly</span></strong><span>, meaning secure and sameSite in production, so no single oversight becomes an open door.</span></p></li><li><p><strong><span>Serve everything over HTTPS</span></strong><span>, because every guarantee above assumes the transport itself is not readable.</span></p></li></ul><p><span>The organizing principle is that no single failure should compromise the whole system.</span></p><h2><strong><span>Takeaways</span></strong></h2><ul><li><p><span>Treat 401 and 403 as different events with different client responses: one triggers a silent refresh and retry, the other shows a permissions error and stops.</span></p></li><li><p><span>Use two tokens with different lifetimes, and persist refresh tokens server-side so sessions can actually be revoked.</span></p></li><li><p><span>Never store tokens in localStorage. Choose deliberately between the hybrid, cookie-only, and backend-for-frontend strategies based on how sensitive your data is.</span></p></li><li><p><span>Rotate refresh tokens so each works once, and treat reuse of a consumed token as a breach signal.</span></p></li><li><p><span>Deduplicate concurrent refresh attempts behind a single in-flight promise, or rotation and reuse detection will log out your own users.</span></p></li><li><p><span>Remember that a JWT payload is readable by anyone holding the token, so it should never contain anything you would not print publicly.</span></p></li></ul><p><span>None of this argues against eventually using a managed provider like Auth0 or Clerk, which are sensible choices for many teams. It argues for understanding the machine before you outsource it, so you can evaluate a provider&#8217;s decisions instead of inheriting them blindly. The login form was never the hard part. The lifecycle is, and now it is yours.</span></p><div><hr></div><p><em><span>This article is adapted from the book </span><a href="https://www.amazon.com/Full-Stack-React-TypeScript-Node-applications-dp-1803235772/dp/1803235772"><span>Full-Stack React, TypeScript, and Node (Second Edition)</span></a><span>. You can order it there to read more.</span></em></p><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p><span>&#128373;&#65039;</span> <strong><a href="https://blog.cloudflare.com/introducing-precursor/"><span>Cloudflare&#8217;s Precursor Knows You&#8217;re a Bot by How You Move</span></a></strong><span>: Cloudflare&#8217;s new behavioral validation engine watches the entire session, not just a single checkpoint, turning how humans and bots actually interact into continuous detection signals. With AI agents now browsing indistinguishably from people, this feels like the quiet obituary for the CAPTCHA: the future of bot detection is behavioral, invisible, and always on. If you build agents or defend against them, this is required reading.</span></p><p><span>&#127912; </span><strong><a href="https://shaders.paper.design/"><span>Paper Shaders Is Now Free and Fully Open Source</span></a></strong><span>: Zero-dependency canvas shaders for React and vanilla JS, and now completely free. Beautiful, GPU-driven backgrounds and effects usually mean pulling in Three.js and a weekend of GLSL headaches; this gives you the eye candy with none of the dependency weight. One of those rare libraries where the demo page alone will sell you.</span></p><p><span>&#128444;&#65039; </span><strong><a href="https://github.com/kane50613/takumi/releases/tag/takumi%402.0.0"><span>Takumi 2.0: JSX to Images, No Browser Required</span></a></strong><span>: Takumi renders JSX and HTML straight to images using a single Rust binary, and this major release squares up directly against Satori and next/og for JSX-based social card rendering. Skipping the headless browser entirely means faster, cheaper OG image generation with fewer moving parts. If your social cards are currently rendered by a Chromium instance you resent paying for, take a look.</span></p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><span>&#128257;</span> <strong><a href="https://addyosmani.com/blog/loop-engineering/"><span>Loop Engineering: Stop Prompting, Start Designing the Loop</span></a></strong><span>: Addy Osmani argues that prompting skills are becoming obsolete; the real skill is designing the system that prompts your agents for you. He breaks it into five building blocks (automations, worktrees, skills, connectors, sub-agents) plus persistent memory, and shows how both Claude Code and the Codex app now ship all of them natively. Refreshingly, he stays skeptical about token costs, which is exactly the caveat this trend needs.</span></p><p><span>&#128184;</span> <strong><a href="https://playcode.io/blog/real-price-of-frontier-models"><span>The Same TypeScript Costs 73% More Tokens on Claude Than GPT</span></a></strong><span>: A genuinely eye-opening measurement study: because every vendor tokenizes differently, the price per million tokens on the rate card tells you almost nothing about your actual bill. The same TypeScript file becomes 1,178 tokens on Anthropic&#8217;s newest tokenizer versus 681 on GPT&#8217;s o200k, and Anthropic&#8217;s tokenizer change added roughly 30% more tokens at the same list price. If you&#8217;re comparing model costs by pricing page alone, you&#8217;re doing it wrong.</span></p><p><span>&#9889;</span> <strong><a href="https://endform.dev/blog/playwright-github-actions"><span>Playwright on GitHub Actions: The Setup That Actually Runs Fast</span></a></strong><span>: Slow Playwright CI is almost never your test suite&#8217;s fault. It&#8217;s the default workflow re-downloading 300 to 500 MB of browsers on every push and running tests on a single worker. This walkthrough fixes both with caching and parallelism, gets a real pipeline well under five minutes, and, crucially, tells you when sharding is actually worth it (later than you think).</span></p><p><span>&#128296; </span><strong><a href="https://www.gingerbill.org/article/2026/07/10/good-tools-are-invisible"><span>Good Tools Are Invisible</span></a></strong><span>: The creator of Odin takes aim at a habit we all recognize: dressing up a tool&#8217;s shortcomings as a fun puzzle and then evangelizing the puzzle. His test is brutal and correct: feeling clever is not the same as being productive, and the honest metric is wall-clock time. Guaranteed to mildly offend at least one vim devotee on your team, which is precisely why you should send it to them.</span></p><p><span>&#129525;</span> <strong><a href="https://www.inngest.com/blog/node-worker-threads-production"><span>Node.js Worker Threads in Production</span></a></strong><span>: Spawning a worker thread is one line of code; making it production infrastructure is everything else. Inngest documents the unglamorous reality: designing the postMessage protocol as a real API boundary, proxying callbacks, shipping the worker as a build artifact, capping crash-respawn loops, and treating shutdown as a drain protocol. This is the article the official docs should link to.</span></p><p><span>&#127846;</span><strong><a href="https://guseyn.com/html/posts/why-vanilla-js.html"><span>Why Vanilla JS</span></a></strong><span>: A contrarian defense of building for the web without frameworks, arguing the problems frameworks solve can be handled with plain JavaScript and a little discipline. Whether you nod along or grind your teeth, it&#8217;s a useful provocation: the strongest counterargument (frameworks are really about teams agreeing on solutions, not solving problems) came from the ensuing Hacker News debate, and both sides are worth your time.</span></p><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><span>&#129504; </span><strong><a href="https://developers.googleblog.com/litertjs-googles-high-performance-web-ai-inference/"><span>LiteRT.js: Google&#8217;s High-Performance Web AI Inference</span></a></strong><span>: Google brings its native on-device inference runtime to the browser, letting you run .tflite models locally with WebAssembly, GPU acceleration via ML Drift, and WebNN support on the way. It&#8217;s positioned as the serious evolution of TensorFlow.js, swapping JavaScript kernels for the real optimized runtime. Local inference means zero server costs, better privacy, and latency your users will actually notice.</span></p><p><span>&#129529;</span> <strong><a href="https://j-brooke.github.io/FracturedJson/"><span>FracturedJson: JSON Formatting Humans Can Actually Read</span></a></strong><span>: A JSON formatter that hits the sweet spot between minified chaos and pretty-printed sprawl: human-readable but fairly compact. If you regularly stare at big lumps of JSON, this makes them genuinely pleasant to scan, and it even tolerates non-standard JSON comments. A small tool that removes a daily annoyance, which is the best kind.</span></p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#148: Good Prompting Isn't the Hard Part Anymore]]></title><description><![CDATA[And why "be specific" means something different when the output has to ship code]]></description><link>https://packtwebdevpro.substack.com/p/148-good-prompting-isnt-the-hard</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/148-good-prompting-isnt-the-hard</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 13 Jul 2026 14:01:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!1JhK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>For years, casual AI users and professional developers were learning what looked like the same skill from opposite ends. Someone drafting emails in ChatGPT and someone drafting a component in an AI coding assistant were both told the same core lesson: be specific, and the output gets better. Because the mechanic looks identical from a distance, it was easy to assume the habit transfers cleanly, that developers simply apply the same technique to a more technical domain.</span></p><p><span>That assumption falls apart under scrutiny, and the reason is worth spelling out, because it changes what &#8220;good prompting&#8221; actually requires the moment your output has to run in production instead of sit in an inbox.</span></p><p><span>Before we get to that, here&#8217;s a look at what&#8217;s making news this week:</span></p><ul><li><p><strong>&#128640; </strong><em><strong><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0">TypeScript 7 Just Made Your Compiler 10x Faster</a></strong></em></p></li><li><p>&#129408; <em><strong><a href="https://bun.com/blog/bun-in-rust">Bun Rewrote Its Entire Codebase in Rust Using 64 Claudes in 11 Days</a></strong></em></p></li><li><p><strong>&#9889; </strong><em><strong><a href="https://voidzero.dev/posts/announcing-vite-plus-beta">Vite+ Goes Beta</a></strong></em></p></li><li><p><strong>&#129517;</strong><em><strong> <a href="https://webkit.org/blog/18136/introducing-the-safari-mcp-server-for-web-developers/">Safari Ships an MCP Server</a></strong></em></p></li><li><p><strong>&#129497;</strong><em><strong> <a href="https://htmx.org/essays/working-with-ai/">Working With AI: A Concrete Example</a></strong></em></p></li><li><p><strong>&#127959;&#65039;</strong><em><strong> <a href="https://bricolage.io/apps-after-agents/">Apps After Agents</a></strong></em></p></li><li><p><strong>&#127908;</strong><em><strong> <a href="https://newsletter.posthog.com/p/how-to-demo">24 Tips for Giving S-Tier Demos</a></strong></em></p></li></ul><h2><strong><span>The Same Advice, Written for Different Stakes</span></strong></h2><p style="text-align: justify;"><span>Sebastian Huelswitt&#8217;s </span><em><span>AI for Anyone: The Beginner&#8217;s Guide to AI</span></em><span> is a fair representative of the genre this assumption comes from. It&#8217;s built for someone who has never opened ChatGPT and just wants to stop feeling behind &#8212; pick a tool, write a first prompt, learn to ask more clearly, use the output for everyday things like emails or generated images. As a genre, these guides have gotten genuinely good at teaching the one mechanic that matters most to their audience: a specific prompt beats a vague one. That advice isn&#8217;t wrong. It&#8217;s calibrated for a world where a mediocre first draft costs you a few minutes of editing, not a production incident.</span></p><h2><strong><span>What Specificity Means When the Output Is Code</span></strong></h2><p style="text-align: justify;"><span>For a developer, &#8220;specific&#8221; stops meaning &#8220;clear about tone and topic&#8221; and starts meaning something much narrower and more technical: which framework and version, which components already exist and shouldn&#8217;t be reinvented, which validation approach the rest of the codebase already uses, what the accessibility bar is, where the edges of scope sit. Leave any of that out and you get something that runs but doesn&#8217;t belong: a form that duplicates three components you already have, ignores your linting rules, or quietly pulls in a dependency nobody approved.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!1JhK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!1JhK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png" width="651" height="350" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:350,&quot;width&quot;:651,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 424w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 848w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 1272w, /__u/substackcdn.com/image/fetch/$s_!1JhK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F86cb105c-fe6d-4040-a7e3-12392529bf24_651x350.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Fig. 1 &#8212; Two prompts asking for the same login form. One is general enough for a beginner&#8217;s guide. The other names a framework, an existing component library, a validation approach, and an accessibility requirement &#8212; the difference between an answer and something you can actually merge.</span></sup></em></p><p style="text-align: justify;"><span>None of this is exotic information. It&#8217;s the same context you&#8217;d put in a ticket or hand to a contractor on their first day. The skill was never a secret syntax. It&#8217;s the discipline of writing down context a beginner prompt is allowed to skip.</span></p><h2><strong><span>The Distance Between Confidence and Correctness</span></strong></h2><p style="text-align: justify;"><span>You&#8217;ve likely run into this without naming it. The output looks put-together. It compiles. It might even pass a quick manual check. Then a reviewer, a linter, a security scan, or a real user on a screen reader finds the thing it was never asked to handle.</span></p><p style="text-align: justify;"><span>That&#8217;s the actual gap between a beginner&#8217;s use case and a developer&#8217;s. A blog post that&#8217;s slightly off in tone is a two-minute fix. A data-fetching function that&#8217;s slightly off in its error handling is an incident with your name near it. The loop looks the same from a distance (prompt, review, adjust), but what &#8220;review&#8221; has to catch is nowhere close to equivalent: injection risks, auth that looks handled but isn&#8217;t, a query that&#8217;s fine in a demo and falls over under real traffic, accessibility that was never part of the request because the tool had no way of knowing it mattered.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!IlMG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 424w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 848w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!IlMG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png" width="651" height="283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:283,&quot;width&quot;:651,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 424w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 848w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IlMG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7bd05401-0447-4be6-b20f-287c8deb2de5_651x283.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Fig. 2 &#8212; Not every AI tool sits at the same distance from your codebase. A chat window only knows what you paste into it. An in-editor assistant sees the file you&#8217;re in. An agentic tool can read the whole repo, run the tests, and open the pull request itself &#8212; which changes what &#8220;writing a prompt&#8221; even means.</span></sup></em></p><p style="text-align: justify;"><span>This is where the tooling itself matters more than a general guide has any reason to mention. A plain chat interface works fine for a one-off question, but everything has to be fed in and pulled back out by hand, and the model never actually sees your repository. In-editor assistants close part of that gap, seeing the file in front of you, usually one at a time. Agentic tools go further still &#8212; something like Claude Code can read across the codebase, run your test suite, coordinate changes across several files, and open a pull request for you to look at. That&#8217;s not a faster version of the same tool. It&#8217;s a different kind of collaborator, and it turns the prompt from a single message into something closer to a scoped handoff.</span></p><h2><strong><span>Reading Generated Code the Way You Would Read a Stranger&#8217;s</span></strong></h2><p style="text-align: justify;"><span>The useful mental model isn&#8217;t new. You already have it. When a pull request lands from someone you&#8217;ve never worked with, you don&#8217;t assume it&#8217;s correct because it runs. You ask what it&#8217;s actually doing, what it&#8217;s assuming about the data, and where it&#8217;s likely to break under conditions the author never considered. AI-generated code deserves exactly that posture, whether it came from a single chat prompt or a longer agentic session that ran while you were somewhere else.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ox3p!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 424w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 848w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ox3p!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png" width="650" height="289" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9230a292-2a27-414e-866b-6be862757837_650x289.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:289,&quot;width&quot;:650,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 424w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 848w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ox3p!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9230a292-2a27-414e-866b-6be862757837_650x289.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Fig. 3 &#8212; The loop that holds up in practice: a prompt written with real context, a draft, automated tests, and a human review gate before anything reaches main. When review turns something up, the fix is usually to revise the prompt itself, not just patch the code, so the next draft doesn&#8217;t repeat the mistake</span></sup><span>.</span></em></p><p style="text-align: justify;"><span>What matters is that review happens before the merge, not after something surfaces in production, and that automated tests are a first pass rather than a replacement for a person reading the code. Tests catch regressions. They don&#8217;t catch a solution that technically passes while solving the wrong problem, and they don&#8217;t catch a gap nobody thought to write a test for. The half of this loop that&#8217;s easy to skip is also the part that compounds: when review finds a problem, the fix that actually helps next time is updating what the prompt included, not just cleaning up the output by hand. For a general audience there usually isn&#8217;t a next prompt to improve. There&#8217;s just the next email.</span></p><h2><strong><span>A Discipline That Holds at Every Scale</span></strong></h2><p style="text-align: justify;"><span>Worth remembering: tools like Claude Code weren&#8217;t built only for the moments things go smoothly. The habits that make an AI assistant useful for a quick script are the same ones that keep you out of trouble on something that&#8217;s actually going to ship. Be specific about the constraints that matter, give it the context a stranger would need, treat the first response as a draft rather than an answer. A vague prompt produces the same generically plausible output whether you&#8217;re asking for a marketing email or an API endpoint. The general advice isn&#8217;t wrong about that. It just never has to deal with what happens when you skip it.</span></p><p style="text-align: justify;"><span>What differs is the judgment applied afterward. Someone using AI to draft an email is checking whether it sounds right. A developer reviewing AI-generated code is checking whether it holds up under load, survives an audit, and still makes sense to whoever opens the file next. Treating AI as a fast, occasionally overconfident collaborator rather than a threat or an oracle is probably the one habit from the casual world that genuinely holds up, once it&#8217;s scaled to match the stakes.</span></p><h2><strong><span>What Actually Carries Over and What You&#8217;ll Have to Add</span></strong></h2><p style="text-align: justify;"><span>Write prompts the way you&#8217;d write a ticket, with the framework, the existing patterns, and the constraints spelled out instead of assumed. Match the tool to the size of the job, and reach for something that can actually see your repo once a task crosses more than one file. Keep the review gate in place no matter how confident the output looks. Confidence in tone was never a reliable stand-in for correctness in logic. And when review turns something up, spend a minute updating the prompt or the shared context behind it, not just the code. That&#8217;s the step a general audience never needed, and the one that actually compounds.</span></p><p style="text-align: justify;"><span>The gap between a prompt that works and code that belongs hasn&#8217;t closed. It&#8217;s just moved earlier, into the way the request gets written in the first place, and it&#8217;s now more clearly yours to own.</span></p><div><hr></div><p><em><span>This article draws on ideas and framing from </span><strong><a href="https://www.amazon.com/AI-Anyone-Beginners-Guide/dp/1807782158/"><span>AI for Anyone: The Beginner&#8217;s Guide to AI</span></a></strong><span> by </span><strong><span>Sebastian Huelswitt</span></strong><span>. If you want the gentler, fuller on-ramp &#8212; picking your first AI tool, writing a first prompt, and using AI for everyday tasks like emails and image generation &#8212; </span><a href="https://www.amazon.com/AI-Anyone-Beginners-Guide/dp/1807782158/"><span>you can find the book here</span></a><span>. </span></em></p><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#128640; <strong><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0">TypeScript 7 Just Made Your Compiler 10x Faster</a></strong>: Microsoft&#8217;s Go rewrite of TypeScript is finally stable, and the numbers are wild. VS Code&#8217;s codebase compiles in 10.6 seconds instead of 125, Slack cut CI type-checking from 7.5 minutes to 1.25, and memory usage actually dropped. One catch worth knowing: there&#8217;s no programmatic API yet, so Vue, Svelte, and Astro projects are stuck on 6.0 until 7.1 lands. For everyone else, this is the easiest upgrade decision of the year.</p><p>&#129408; <strong><a href="https://bun.com/blog/bun-in-rust">Bun Rewrote Its Entire Codebase in Rust Using 64 Claudes in 11 Days</a>: </strong>Jarred Sumner ported all 535,000 lines of Bun from Zig to Rust in 11 days using around 64 parallel Claude agents, with adversarial reviewer agents checking every line. The payoff: 128 bugs fixed, 20% smaller binaries, and the full test suite passing with nothing skipped. Bun is Anthropic-owned now, so grain of salt, but the workflow breakdown is genuinely worth reading.</p><p><span>&#9889;</span> <strong><a href="https://voidzero.dev/posts/announcing-vite-plus-beta">Vite+ Goes Beta</a></strong>: VoidZero&#8217;s unified toolchain hit beta, bundling Vite, Vitest, Rolldown, Oxlint, and friends behind a single vp command that handles dev, test, lint, build, and monorepo task-running. With 1,300+ public repos already depending on it, &#8220;unified toolchain&#8221; is starting to look less like a pitch and more like a fact. If your package.json scripts section reads like a ransom note, this is your intervention.</p><p style="text-align: justify;"><span>&#128506;&#65039;</span> <strong><a href="https://visgl.github.io/react-google-maps/">react-google-maps: Google Maps, But Make It React</a></strong>: The vis.gl team maintains this library of React components and hooks that wrap the Google Maps JavaScript API, letting you treat a map as a fully controlled reactive component. It also plays nicely with deck.gl for WebGL visualizations on top. Blessed are the maintainers who save us from wrapping imperative APIs in useEffect ourselves.</p><p style="text-align: justify;"><span>&#127912;</span> <strong><a href="https://www.env.style/">env.style: Never Ship to Prod by Accident Again</a></strong>: Ryan George whipped up a project called env.style to change your project&#8217;s favicon color based on what environment it&#8217;s in, so your dev, staging, and production tabs are visually distinct at a glance. Honestly useful. But if you need this, it&#8217;s probably also time to close a few tabs.</p><p style="text-align: justify;"><span>&#128737;&#65039;</span> <strong><a href="https://osv.dev/">OSV.dev: Google&#8217;s Open Source Vulnerability Database</a></strong>: Google built a distributed, open database of vulnerabilities in open-source software, with precise version-level affected ranges and an API you can query straight from your dependency tooling. Genuinely great infrastructure for the whole ecosystem. I hope whoever built this impressed the committee and got their promotion before the project gets sent to the Google graveyard.</p><p style="text-align: justify;"><span>&#129517;</span> <strong><a href="https://webkit.org/blog/18136/introducing-the-safari-mcp-server-for-web-developers/">Safari Ships an MCP Server</a></strong>: Safari Technology Preview 247 includes an MCP server that lets any compatible coding agent drive a real Safari window: reading the DOM, watching network requests, taking screenshots, and checking console output to debug your site autonomously. Apple showing up to the agentic party with a local-first, no-network-calls design is a pleasant surprise. Your &#8220;works in Chrome&#8221; excuses just lost their last line of defense.</p><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><span>&#129497;</span> <strong><a href="https://htmx.org/essays/working-with-ai/">Working With AI: A Concrete Example</a></strong>: Carson Gross (of htmx fame) walks through a real hyperscript parser bug where Claude nailed the root-cause investigation and tests but kept proposing hacky fixes until a human who knew the codebase stepped in. The most honest write-up of AI-assisted maintenance I&#8217;ve read in a while: be the sorcerer, not the apprentice.</p><p style="text-align: justify;"><span>&#128257;</span> <strong><a href="https://lilianweng.github.io/posts/2026-07-04-harness/">Harness Engineering for Self-Improvement</a></strong>: Lilian Weng surveys the research on harnesses, the orchestration layer around models that manages workflows, memory, subagents, and self-improvement loops, from context engineering through evolutionary search over harness code itself. Dense, rigorous, and exactly the kind of post you bookmark and actually finish. Clear your calendar for this one.</p><p style="text-align: justify;"><span>&#127959;&#65039;</span> <strong><a href="https://bricolage.io/apps-after-agents/">Apps After Agents</a></strong>: Kyle Mathews argues &#8220;software is dead&#8221; is wrong on both supply and demand: agents can&#8217;t absorb judgment-heavy &#8220;camera mode&#8221; work, and users prefer designed interfaces over formulating prompts. The real moat, he says, is your &#8220;substrate&#8221; of accumulated decisions and conventions. A genuinely useful frame for anyone tired of both the doomers and the maximalists.</p><p style="text-align: justify;"><span>&#128187;</span> <strong><a href="https://stevekrouse.com/learn-to-code">Learning to Code Is Still Worthwhile</a></strong>: Val Town founder Steve Krouse pushes back on the &#8220;nobody says learn to code anymore&#8221; narrative, arguing coding is worth learning the way math or literature is: for the meta-skills, the creative expression, and the joy. Short, sincere, and a nice palate cleanser after all the AI discourse above.</p><p><span>&#127908;</span><strong><a href="https://newsletter.posthog.com/p/how-to-demo"><span>24 Tips for Giving S-Tier Demos</span></a></strong><span>: PostHog&#8217;s Jina Yoon watched 50+ hackathon demos back to back and distilled what separated the great from the forgettable: one main point, get to it fast, demo against the painful alternative, and never apologize before you start. Required reading before your next sprint review. Tip 12 alone is worth the click.</span></p><div><hr></div><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><span>&#127793;</span> <strong><a href="https://wordgard.net/">Wordgard</a></strong>: A schema-based, semantic rich-text editor library for the browser from Marijn Haverbeke, where you define exactly what content your documents allow instead of wrestling free-form HTML. Accessible, RTL-aware, collaborative-editing ready, and MIT licensed. The footer proudly declares &#8220;Contains 0% AI&#8221;, which in 2026 is practically a feature flag.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#147: Your Next Feature Request Might Already Be a Working Prototype]]></title><description><![CDATA[And what to check before you build on top of an AI-generated prototype]]></description><link>https://packtwebdevpro.substack.com/p/147-your-next-feature-request-might</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/147-your-next-feature-request-might</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 06 Jul 2026 14:02:05 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LDNd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong>AI can write your code in seconds. But should it?</strong></h1><p style="text-align: center;">We just launched <strong><a href="/__u/packtbuildwithai.substack.com/">Build with AI</a></strong>, and our new series, The Agentic Engineering Playbook, is already asking the hard questions.</p><p>Two parts are live already:</p><ul><li><p>Why vibe coding feels magical for prototypes, but breaks down at scale</p></li><li><p>The four eras of agentic engineering, and where most teams actually stand today</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/packtbuildwithai.substack.com/" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 424w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 848w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LDNd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp" width="502" height="331.32" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:528,&quot;width&quot;:800,&quot;resizeWidth&quot;:502,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;image&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:&quot;https://packtbuildwithai.substack.com/&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="image" title="image" srcset="/__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 424w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 848w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!LDNd!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F27e1e61e-ac4d-4ab5-9a94-b43bce4fab50_800x528.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Three more parts are coming, unpacking how to turn AI from a fast code generator into a real engineering multiplier.</p><p>Subscribe today to follow the complete series as new parts are released and claim your free welcome eBook.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://packtbuildwithai.substack.com/&quot;,&quot;text&quot;:&quot;Subscribe and Claim Your Free eBook&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="/__u/packtbuildwithai.substack.com/"><span>Subscribe and Claim Your Free eBook</span></a></p></div><p>For years, the relationship between design and development followed a predictable rhythm. Designers explored ideas, tested them in static mockups, and eventually handed off a set of screens, specs, and good intentions. Developers took it from there, filling in the gaps that design never quite covers: edge cases, data structures, error states, the messy reality of production systems. It worked, mostly, because everyone understood their role in the relay.</p><p>That rhythm is breaking down, and it&#8217;s worth understanding why, because the shift changes what actually lands on your desk before you write a line of code.</p><p>Before we get to that, here&#8217;s a look at what&#8217;s making news this week:</p><ul><li><p><a href="https://www.anthropic.com/news/claude-sonnet-5">Claude Sonnet 5 is here, though lifted export controls on Fable 5 are stealing its thunder</a></p></li><li><p><a href="https://github.blog/changelog/2026-06-29-restrict-issue-creation-to-collaborators-only/">GitHub now lets you restrict issue creation (link from GitHub blurb) to collaborators with write access</a></p></li><li><p><a href="https://vercel.com/changelog/bring-your-dockerfile-to-vercel-functions">Vercel Functions now support Docker, so you can deploy containers directly</a></p></li><li><p><a href="https://code.visualstudio.com/blogs/2026/06/26/iterating-faster-with-ts-7">How TypeScript 7&#8217;s Go-based compiler made VS Code over 10x faster to build</a></p></li><li><p><a href="https://oxc.rs/">Oxlint, a Rust-based linter that turns 30-second ESLint runs into sub-second ones</a></p></li></ul><h2>Prototypes That Behave Like Real Products</h2><p>A new generation of AI-assisted tools, including Lovable, v0.dev, Bolt.new, Figma Make, and Replit, lets designers build interactive prototypes that don&#8217;t just look like an app but function like one. Screens respond to input, flows execute end to end, and users can complete real tasks inside something that was assembled from a natural language description rather than a stack of tickets. What used to take a sprint or two of coordinated effort between design, product, and engineering can now happen in an afternoon, in the hands of a single person who understands the problem well enough to describe it clearly.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!K0iP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 424w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 848w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 1272w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!K0iP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png" width="722" height="640" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:640,&quot;width&quot;:722,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 424w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 848w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 1272w, /__u/substackcdn.com/image/fetch/$s_!K0iP!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd2219e28-f1b1-4a00-a69d-0f5dc34c4f85_722x640.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Fig. 1: I want to build the app described in the attachment&#8221; &#8212; a plain-language prompt turns a one-page brief into a working homepage, complete with a nav, hero section, and live stats.</sup></em></p><p>This is not a gimmick. Designers are using these tools to validate ideas with real users before a single developer gets involved, testing whether a value proposition resonates and whether a flow makes sense long before anyone commits to an architecture. That&#8217;s a genuinely good thing. Cheaper validation means fewer wasted sprints building something nobody wanted, and it means the thing that eventually reaches you has already survived contact with actual users.</p><p>The complication is what happens next. In some organizations, that validated prototype isn&#8217;t just inspiration for a spec anymore. It&#8217;s the artifact itself, checked into a repository and treated as a starting point for the real build. Some designers are going further still, using tools like Claude Code directly inside an editor to scaffold application structure, generate front-end and back-end code, and wire up basic services, all before a developer is looped in at all.</p><h2>The Gap Between &#8220;It Works&#8221; and &#8220;It&#8217;s Ready&#8221;</h2><p>If you&#8217;re a developer, you&#8217;ve probably already brushed up against this without naming it. Maybe a product manager forwarded you a Lovable prototype and asked how long it would take to &#8220;make it real.&#8221; Maybe a designer handed you a working demo built with v0 and seemed genuinely confused when you explained that &#8220;working&#8221; and &#8220;production-ready&#8221; are not the same claim.</p><p>They&#8217;re not being naive. The prototype really does work, in the sense that matters to a designer validating an idea: it demonstrates behavior, and it lets users complete tasks. But there&#8217;s a meaningful gap between a prototype that demonstrates an idea and a system that can hold up in production, and closing that gap is still, unmistakably engineering work.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!0DaT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 424w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 848w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 1272w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!0DaT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png" width="786" height="714" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:714,&quot;width&quot;:786,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 424w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 848w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 1272w, /__u/substackcdn.com/image/fetch/$s_!0DaT!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3be7142-7859-4793-a806-ec705b8bd6cd_786x714.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Fig. 2: Listings, pricing, and booking flows, fully styled and clickable. Convincing enough to hand to a real user for testing, though nothing behind these screens has been built yet.</sup></em></p><p>A few things tend to be true of AI-scaffolded prototypes, regardless of which tool generated them. Authentication is often stubbed out or handled in a way that would never survive a security review. Data validation is inconsistent because the AI is optimized for showing behavior rather than defending against malformed input. Error handling covers the happy path and little else.</p><p>Accessibility is frequently an afterthought, since the tools were optimized to produce something visually convincing and functionally demonstrable, not something that passes a screen reader test. And the underlying architecture, however clean it looks, was generated to satisfy a prompt, not to scale, so assumptions about data models or service boundaries may not hold once real usage patterns show up.</p><p>None of this means the prototype is worthless. It means your job now includes a step that used to be implicit: figuring out exactly what you actually inherited before you start building on top of it.</p><h2>Treat It Like a Pull Request From Someone You&#8217;ve Never Met</h2><p>The most useful mental model here is one you already have. When you review a pull request from a junior developer, you don&#8217;t assume the code is correct just because it runs. You ask what it&#8217;s actually doing, what assumptions it&#8217;s making, and where it&#8217;s likely to break under conditions the author didn&#8217;t consider. AI-generated scaffolding deserves exactly that same posture, whether it arrived as a Lovable export, a v0 component, or a Claude Code session someone ran before you were in the room.</p><p>That means asking pointed questions early, ideally before you commit to building on top of what you were handed. What&#8217;s actually persisted versus what&#8217;s mocked in the local state? What happens when a request fails, or when a user submits something malformed? What was tested with real users, and what was simply assumed to work because it was demonstrated correctly in a single session? Is there an authentication layer here, or does it just look like one? These aren&#8217;t gotcha questions aimed at the designer who built the prototype. They&#8217;re the same questions you&#8217;d ask of any unfamiliar codebase, and asking them early saves you from discovering the answers the hard way three sprints later.</p><p>It also helps to have this conversation with designers directly, rather than assuming they know where the line sits. Most designers using these tools are not trying to skip engineering. They&#8217;re solving a real problem: getting to validated learning faster, with fewer people and less overhead. They generally want to know where their prototype&#8217;s assumptions will cause you trouble, because a designer who understands that gap can scope their next round of prototyping more usefully, and can stop treating &#8220;it works in the demo&#8221; as equivalent to &#8220;it&#8217;s ready to ship.&#8221;</p><h2>The Engineering Value Is Shifting, Not Shrinking</h2><p>There&#8217;s a temptation to read all of this as a threat, as though AI-assisted prototyping is quietly encroaching on territory that used to belong to developers. That framing misses what&#8217;s actually happening. The work that&#8217;s disappearing is the work of translating vague requirements into a first functional version, the scaffolding and boilerplate that ate weeks of calendar time without much intellectual weight to it. What&#8217;s left, and arguably what was always the hard part, is exactly the stuff AI tools are bad at: reasoning about scale, securing a system against real-world abuse, designing data models that won&#8217;t need to be rebuilt in six months, making something usable for people with disabilities, and maintaining a codebase over years rather than a demo over an afternoon.</p><p>That&#8217;s not a smaller job. It&#8217;s a more concentrated one. The parts of development that require judgment, not just output, are becoming a larger share of what you actually spend your time on, because the parts that didn&#8217;t require judgment are increasingly handled before you arrive.</p><p>This also changes what&#8217;s worth getting sharper at. Being able to read an unfamiliar, AI-generated codebase quickly and spot where it&#8217;s fragile is now a core skill, not a nice-to-have. So is being explicit and precise when you push back on scope or timeline, because &#8220;the prototype already works&#8221; is going to be a recurring argument you&#8217;ll need to answer with specifics rather than vague discomfort. Security and accessibility reviews stop being a checklist you run near the end of a project and start being something you do as a first pass on anything that arrives from a design tool, since those are exactly the areas most likely to have been skipped.</p><h2>The Same Tools Are Yours to Use, Too</h2><p>It&#8217;s worth remembering that Claude Code, and tools like it weren&#8217;t built exclusively for designers making the leap into software. They&#8217;re development tools first, and the same techniques designers are using to scaffold a prototype quickly are just as available to you when you&#8217;re standing up a new service, exploring an unfamiliar API, or getting a rough version of a feature in front of a stakeholder before committing to a final approach.</p><p>The habits that make these tools useful are the same ones described in every good code review or design brief: be specific about what you want, provide the context that actually matters, and treat the first response as a starting point rather than a finished answer. Vague prompts produce the same vague, generic output whether you&#8217;re a designer exploring a persona or a developer scaffolding an endpoint. The developers getting the most out of AI coding assistants tend to work the same way experienced designers do: iteratively, asking the tool to explain its reasoning, comparing alternatives, and pushing back when something feels underspecified rather than accepting the first plausible-looking result.</p><p>This matters because the line between &#8220;AI helped a designer build something&#8221; and &#8220;AI helped a developer build something&#8221; is thinner than it looks. The tools are converging. What still differs is the judgment applied to the output. A designer evaluating an AI-generated flow is asking whether it serves the user. A developer evaluating AI-generated code is asking whether it will hold up under load, survive an audit, and still make sense to the next person who opens the file. Treating AI as a fast, occasionally overconfident collaborator, rather than either a threat or an oracle, is probably the single most transferable skill in all of this.</p><h2>A Few Things Worth Carrying Forward</h2><p>The shift toward AI-assisted prototyping isn&#8217;t going to reverse, and treating it as a fad to wait out is the wrong instinct. A more useful posture is to get specific about where the value of your engineering judgment actually sits, and to make that value visible earlier in the process rather than discovering it under deadline pressure.</p><p>A few concrete habits are worth building now. Ask what&#8217;s real and what&#8217;s demonstration-grade the moment a prototype lands on your desk, before you start estimating work against it. Run a quick security and accessibility pass on any AI-generated scaffold as a default step, not an optional one, since those are the areas most consistently under-covered. Talk to whoever built the prototype about what they validated with users and what they simply assumed, because that conversation will tell you more about risk than reading the code alone. And resist the urge to either dismiss these tools as toys or treat their output as trustworthy by default. Both instincts will cost you time.</p><p>The gap between an idea that works and a system that lasts hasn&#8217;t closed. It&#8217;s just moved earlier in the process, and it&#8217;s now more clearly yours to own.</p><div class="pullquote"><p>This<span> </span>article draws on ideas and case study material from <strong><a href="https://www.amazon.com/AI-Design-Designers-Artificial-Intelligence/dp/1807787591">AI in UX Design</a>: How UX Designers are Using AI in the Age of Artificial Intelligence</strong> by <strong>Lise Pilot</strong>. If you want the fuller picture of how designers are using these same tools across the whole product lifecycle, from research and personas through no-code prototyping and validation, <a href="https://www.amazon.com/AI-Design-Designers-Artificial-Intelligence/dp/1807787591"><span>you can find the book </span></a><strong><a href="https://www.amazon.com/AI-Design-Designers-Artificial-Intelligence/dp/1807787591"><span>here</span></a></strong>.</p></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p><span>&#129504;</span> <a href="https://www.anthropic.com/news/claude-sonnet-5">Claude Sonnet 5 arrives to a distracted crowd</a><br>Anthropic shipped Claude Sonnet 5, its latest mid-tier workhorse. Solid release, unfortunate timing: with export controls on Fable 5 now lifted, most devs are eyeing the bigger sibling instead.</p><p><span>&#128274;</span> <a href="https://github.blog/changelog/2026-06-29-restrict-issue-creation-to-collaborators-only/">GitHub lets you lock down issue creation</a><br>GitHub&#8217;s latest move to keep project management sane in the agentic era: you can now restrict issue creation to collaborators with write access. Fewer drive-by tickets, cleaner backlogs.</p><p><span>&#127912;</span> <a href="https://astryx.atmeta.com/blog/introducing-astryx">Meta open-sources Astryx, a design system built for agents</a><br>Meta released Astryx, an open-source design system built to play nicely with AI agents. It is reportedly battle-tested across 13,000 internal apps, so it arrives with more mileage than most v1 releases.</p><p><span>&#128051;</span> <a href="https://vercel.com/changelog/bring-your-dockerfile-to-vercel-functions">Vercel Functions now speak Docker</a><br>Bring your Dockerfile to Vercel Functions and deploy containers directly. A genuinely useful addition, even if longtime users will note it took only 6.5 years to reinvent ZEIT.</p><div><hr></div><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><span>&#9889;</span> <a href="https://code.visualstudio.com/blogs/2026/06/26/iterating-faster-with-ts-7">How TypeScript 7 made VS Code faster to build</a><br>The VS Code team adopted TypeScript 7, the Go-based compiler rewrite that is over 10x faster in many cases. They started migrating while TS 7 was still under construction, and both teams came out ahead: faster builds for VS Code, a better-tested release for TypeScript.</p><p><span>&#128190;</span> <a href="https://engineering.fb.com/2026/07/01/data-infrastructure/metas-ai-storage-blueprint-at-scale/">Meta&#8217;s AI storage blueprint at scale</a><br>If AI is the brain, storage is the memory. Meta&#8217;s infra team breaks down how they keep exabytes of training data fast and reliable as frontier model releases shrink from months apart to weeks.</p><p><span>&#127767;</span> <a href="https://olliewilliams.xyz/blog/dark-mode/">Dark mode with web standards</a><br>Ollie Williams shows how to build a proper dark mode toggle with a meta tag, the color-scheme property, and barely any JavaScript. Includes the gotchas, like why prefers-color-scheme ignores your in-page toggle.</p><p><span>&#128250;</span> <a href="https://codetv.dev/series/build-log/miami/tanner-linsley">The business of TanStack</a><br>CodeTV&#8217;s Build Log follows Tanner Linsley, who built TanStack into a thriving open source community and now faces the harder problem: turning it into a sustainable business.</p><div><hr></div><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><span>&#129408;</span><strong><a href="https://oxc.rs">Oxlint</a>: </strong>A Rust-based linter for JS/TS from VoidZero (Evan You&#8217;s tooling company) that turns 30-second ESLint runs into sub-second ones. As of March, its plugin system runs most existing ESLint plugins as-is, so the &#8220;but my custom rules&#8221; objection is mostly gone. No migration needed either: <span>npm install -D oxlint</span>, let it handle the fast first pass, and keep ESLint for the gaps. Preact, PostHog, and Midjourney already run it.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#146: Your Microservice Landscape Needs a Single Door]]></title><description><![CDATA[And how the Edge Server Pattern Tames Distributed System Complexity]]></description><link>https://packtwebdevpro.substack.com/p/146-your-microservice-landscape-needs</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/146-your-microservice-landscape-needs</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Tue, 30 Jun 2026 14:03:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!K9eT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong><a href="https://www.vpdae.com/redirect/nck675s2gzn00ukhfu9tyrmbc3w">Social engineering is about manipulating people's emotions. Identify the susceptibilities that hackers use to exploit people.</a></strong></h1><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://www.vpdae.com/redirect/nck675s2gzn00ukhfu9tyrmbc3w" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 424w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 848w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!hhL4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png" width="252" height="168" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:200,&quot;width&quot;:300,&quot;resizeWidth&quot;:252,&quot;bytes&quot;:9987,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://www.vpdae.com/redirect/nck675s2gzn00ukhfu9tyrmbc3w&quot;,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/203655475?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 424w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 848w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!hhL4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdbe090a9-ba55-4bfb-aaef-4c1a9327a3bc_300x200.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p style="text-align: center;">This NINJIO Insights Report dives into the key emotional susceptibilities that make social engineering work and offers concrete steps that your security team can take to equip your workforce to resist cyberattacks.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.vpdae.com/redirect/nck675s2gzn00ukhfu9tyrmbc3w&quot;,&quot;text&quot;:&quot;Download the Guide&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.vpdae.com/redirect/nck675s2gzn00ukhfu9tyrmbc3w"><span>Download the Guide</span></a></p></div><p><span>Microservices promise modularity. You decompose a system into small, focused services, each owning its domain, each deployable independently. It sounds clean. Then you look at the network diagram and realize that every service has a public address, every team is implementing JWT validation in their own way, and refactoring a single service boundary requires coordinating changes across four different client teams.</span></p><p><span>That is not a distributed system. That is a distributed mess with extra steps.</span></p><p><span>The edge server pattern exists to fix this at the architectural level. The idea is straightforward: all external traffic enters your system through one controlled point. Everything behind it is private. One door in, many rooms inside. This article goes deep on what that actually means in practice, why the benefits compound as your system grows, and what you are genuinely giving up in exchange.</span></p><h2><span>The Problem with Open Borders</span></h2><p><span>Imagine a typical early-stage microservices setup. The product team exposes port 8081. The review service is on 8082. The recommendation engine lives at 8083. Your frontend knows these addresses. Your mobile app knows them. A few third-party partners have been given direct integration URLs. Everyone is talking to everyone, and it works well enough to ship.</span></p><p><span>Now the problems start accumulating, and they are not obvious until they hurt.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!K9eT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 424w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 848w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 1272w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!K9eT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png" width="673" height="366" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:366,&quot;width&quot;:673,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 424w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 848w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 1272w, /__u/substackcdn.com/image/fetch/$s_!K9eT!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd628b704-a4e8-4f18-b31c-4968fa22bae6_673x366.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Fig. 1: Open borders vs. a single edge. On the left, every service is reachable and must handle auth independently. On the right, the edge is the only entry point.</span></sup></em></p><h3><span>Security becomes a distributed responsibility</span></h3><p><span>When every service is externally reachable, every service has to handle authentication. In practice this means each team implements their own token validation logic, maintains their own secret rotation process, and writes their own integration tests for auth edge cases. The surface area for misconfiguration grows linearly with the number of services.</span></p><p><span>A single service that skips a validation check, misconfigures a CORS header, or accidentally exposes a debug endpoint becomes a gap in the entire system. You cannot audit security policy in one place because there is no one place. You have to review every service independently and trust that every team got it right every time.</span></p><h3><span>Internal structure leaks into external contracts</span></h3><p><span>Suppose the product service has grown large enough that the team wants to split it: one service handles product metadata, another handles inventory. Internally this is a clean refactoring. Externally it is a breaking change for every client that has been hitting /product-service/ directly.</span></p><p><span>You now have three options, none of them good: freeze the refactoring until you can coordinate a client migration, run the old service indefinitely as a shim, or accept that you will break integrations. The real problem is not the refactoring; it is that you let internal structure leak into external contracts in the first place. Direct service access makes that inevitable.</span></p><h3><span>Network security becomes exponentially harder</span></h3><p><span>Every externally reachable service needs its own firewall rules, its own TLS certificate, its own rate limiting configuration. For five services this is manageable. For twenty it is a full-time job. For fifty it is a source of chronic incidents. Services that have no business accepting external traffic, internal caches, configuration stores, event buses, still need to be deliberately locked down rather than simply unexposed.</span></p><blockquote><p><em><span>&#8220;The fundamental issue is not any single vulnerability or misconfiguration. It is that an open-border architecture makes deliberate exposure the exception and accidental exposure the default. The edge server inverts that.&#8221;</span></em></p></blockquote><h2><span>What an Edge Server Actually Does</span></h2><p><span>At its core, an edge server is a reverse proxy. External requests arrive, the edge inspects them and routes them to the right internal service. Clients never know the internal topology. But a reverse proxy is just the foundation. A well-implemented edge server is doing several distinct jobs simultaneously.</span></p><h3><span>Path-based routing</span></h3><p><span>The most fundamental capability is routing requests based on their URL path. A request to /product-composite/** goes to the product composite service. A request to /eureka/** goes to the discovery server. These rules live in configuration, not in client code, which means they can change without clients noticing.</span></p><p><span>This is more powerful than it first appears. Your internal service might live at an address that makes sense to your infrastructure team but would be meaningless or confusing to external consumers. The edge lets you present a URL structure that reflects your API design, not your deployment topology. The two do not have to match.</span></p><div class="callout-block" data-callout="true"><p><code>spring: cloud: gateway: routes: - id: product-composite uri: lb://product-composite predicates: - Path=/product-composite/** - id: eureka-web uri: http://eureka:8761 predicates: - Path=/eureka/**</code></p></div><p><span>Notice lb://product-composite. The lb:// scheme tells the gateway to resolve the service via the load balancer, which in a Spring Cloud setup means using service discovery. The edge does not need to know the IP address of the product composite service; it just asks the registry for a healthy instance and routes there. This means your edge routing config stays stable even as services scale up, move, or restart.</span></p><h3><span>Service hiding</span></h3><p><span>Not every service should be reachable from outside. Your order processing service, your internal event bus adapter, your ML feature store: these components exist to serve other services, not external consumers. In an open-border setup you have to actively lock them down. With an edge server, they simply have no externally routable address. They exist behind the edge and are unreachable by definition.</span></p><h3><span>Centralized authentication and authorization</span></h3><p><span>Rather than implementing JWT validation in every service, you implement it once at the edge. The edge verifies the token signature, checks expiry, and rejects unauthorized requests before they ever reach your internal network. Internal services can then trust that any request they receive has already been authenticated.</span></p><p><span>This is sometimes taken further with header propagation: the edge extracts claims from the token and forwards them as headers (X-User-Id, X-User-Roles) so internal services have user context without re-parsing the token themselves. Each service still makes its own authorization decisions based on those headers, but the cryptographic verification happens exactly once.</span></p><blockquote><p><em><span>Centralizing authentication at the edge does not mean internal services should blindly trust everything they receive. Defense in depth still applies. But it does mean you have one authoritative place where external identity claims are verified.</span></em></p></blockquote><h3><span>HTTPS termination and protocol handling</span></h3><p><span>External clients always communicate over HTTPS with the edge. What happens internally is an implementation detail. The edge terminates TLS, handles certificate renewal, and communicates with internal services over HTTP on a private network. This means internal services do not each need their own certificates, do not each need TLS configuration, and do not each introduce latency from TLS handshakes on every inter-service call.</span></p><h3><span>Graceful failure handling</span></h3><p><span>When an upstream service is unavailable, the alternative to a well-configured edge is that the raw error propagates directly to the client: a 500 with an internal stack trace, a connection timeout with no explanation, a response body that includes your service class names and database schema details.</span></p><p><span>The edge is the right place to intercept upstream failures and decide what the external representation should look like. A cached response for read-heavy endpoints that can tolerate slight staleness. A structured error response that tells the client what went wrong without revealing internal state. A degraded response that returns partial data when one upstream is down but others are healthy.</span></p><h2><span>Routing Rules as Design Decisions</span></h2><p><span>One thing that becomes apparent quickly when you start working with an edge server: the routing configuration is not just plumbing. Every decision you make about URL structure, versioning, and predicate logic is a decision about how you want the outside world to think about your system.</span></p><h2><span>URL structure as API design</span></h2><p><span>The path you expose externally does not have to match your internal service name. You can expose /api/v1/products while internally routing to a service called product-composite-v3. You can version your public API independently of your internal deployment cadence. You can reorganize your internal services without changing a single external URL, as long as you update the routing rules.</span></p><p><span>This separation matters most when you are managing third-party integrations. External consumers build against your URLs. If those URLs map directly to internal service names, every internal refactoring becomes a negotiation with your consumers. If they map to stable, intentionally designed API paths, you keep full control over your internal evolution.</span></p><h3><span>Predicate-based routing beyond paths</span></h3><p><span>Path matching is the baseline. Route predicates let you go further. You can route based on the hostname in the request, which is useful for supporting multiple environments through a single edge deployment with different backend targets. You can route based on request headers, which is the foundation for several useful patterns:</span></p><ul><li><p><span>Canary deployments: route requests with a specific header (X-Beta-User: true) to a new version of a service, while all other traffic goes to the stable version.</span></p></li><li><p><span>A/B testing: route a percentage of traffic based on a user segment header, without any changes to the services themselves.</span></p></li><li><p><span>Client-specific routing: route mobile clients (detected via User-Agent) to a backend optimized for mobile response shapes, while web clients hit a different implementation.</span></p></li></ul><blockquote><p><em><span>The routing layer is where you manage change without breaking things. A feature flag, a traffic split, a version migration: all of these can be implemented at the edge without touching a single service deployment.</span></em></p></blockquote><h2><span>Selective Exposure: The Discovery Server Example</span></h2><p><span>One decision that reveals the real nature of the edge server is what to do with internal infrastructure. Take the service discovery server (Eureka, Consul, or similar). It is primarily an internal component: services register with it, other services query it to find healthy instances. There is no reason for external clients to interact with it directly.</span></p><p><span>But operators have a legitimate need to observe its state. When something is misbehaving, you want to know: which instances are registered? Which are reporting as healthy? Which went missing in the last five minutes? Completely blocking external access to the discovery server means operators cannot check this without SSH access to the internal network.</span></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Pc-L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Pc-L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png" width="821" height="386" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:386,&quot;width&quot;:821,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 424w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 848w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Pc-L!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F964c83d2-37bf-4296-ae75-dcd48e5e36ab_821x386.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Fig. 2: Only the read-only /eureka/** endpoints are exposed through the edge. Registration, deletion, and internal query endpoints stay private.</span></sup></em></p><p><span>The solution is selective exposure. Route a read-only subset of the discovery server API through the edge under a controlled path:</span></p><div class="callout-block" data-callout="true"><p><span>-</span><code> id: eureka-web uri: http://eureka:8761 predicates: - Path=/eureka/**</code></p></div><p><span>Operators can now hit /eureka/apps to see registered services, or pull up the Eureka web UI at /eureka/ for a visual status overview. The broader discovery infrastructure (registration endpoints, internal service-to-service queries) stays private. You expose exactly the surface that serves a legitimate external use case, and nothing more.</span></p><p><span>This is the right mental model for the edge server across every service in your system. The question is never just &#8216;should this be external or internal?&#8217; The question is &#8216;which specific surface of this service needs to be external, and to whom?&#8217; The edge gives you the precision to answer that per endpoint rather than per service.</span></p><h3><strong><span>Tradeoffs and Practical Considerations</span></strong></h3><h4><span>Availability</span></h4><p><span>Edge servers are stateless, so the single-point-of-failure concern is manageable: run two or three instances behind a load balancer and failover is fast. The risk is real but the mitigation is straightforward.</span></p><h4><span>Performance overhead</span></h4><p><span>The extra hop is typically single-digit milliseconds and rarely the bottleneck. The bigger concern is thread management under high throughput. Reactive implementations like Spring Cloud Gateway (Project Reactor) handle this well since they never block a thread waiting on an upstream response.</span></p><h4><span>Local development friction</span></h4><p><span>The most underestimated tradeoff. With an edge in the mix, services need to be registry-discoverable, which complicates local runs. Solve it with a full Docker Compose stack, a dev-mode bypass config, or by stubbing the edge in tests. Painful only if you leave it unplanned.</span></p><h4><span>Edge server vs. API gateway</span></h4><p><span>Same architectural pattern, different feature depth. A lightweight edge server (Spring Cloud Gateway, Nginx, Traefik) covers routing, auth, and protocol management, which is enough for most internal systems. A full gateway product (Kong, Apigee, AWS API Gateway) adds per-consumer rate limiting, developer portals, and billing integration. Worth it only if the API itself is a product.</span></p><h2><span>Key Takeaways</span></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YHOn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 424w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 848w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YHOn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png" width="755" height="588" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:588,&quot;width&quot;:755,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59667,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://packtwebdevpro.substack.com/i/203655475?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 424w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 848w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YHOn!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ef1720a-615e-463b-9785-0a5d9fe264ca_755x588.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="pullquote"><p>The single-door principle is one of those constraints that feels unnecessary until you have operated a system without it. Once you have spent time debugging a security misconfiguration on service nine of fifteen, or coordinated a client migration because an internal refactor broke a public URL, the value of a single controlled entry point becomes very clear. Start with the edge, even when your system is small. The habits it enforces scale. The problems it prevents do not announce themselves until it is too late.</p></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p>&#9889; <a href="https://blog.cloudflare.com/temporary-accounts/">Cloudflare Workers Now Live in 60 Seconds, No Account Needed</a>: Run <code>wrangler deploy --temporary</code> and get a live Worker instantly. It stays up for 60 minutes, and you can claim it to make it permanent. Exactly the kind of friction removal that makes agents and fast-moving developers happy.</p><p>&#128450;&#65039; <a href="https://trypolygraph.com/">Polygraph Gives AI Agents a Map of Your Entire Codebase</a>: Built by the Nx team, Polygraph indexes every repo you have access to and maps how they connect through packages and APIs, effectively turning scattered codebases into a synthetic monorepo. It&#8217;s a meta-harness for agents, not an agent itself, and it comes with cross-session memory. Free during early access.</p><p>&#128214; <a href="https://developer.mozilla.org/en-US/blog/introducing-mdn-mcp-server/">MDN Now Has an Official MCP Server for Your Coding Agent</a>: MDN&#8217;s new MCP server pipes its documentation and browser compatibility data directly into your editor or IDE. Your LLM gets accurate, current web platform info instead of pulling from stale training data when writing HTML, CSS, or JavaScript. A quiet but meaningful upgrade for AI-assisted web development.</p><p>&#129302; <a href="https://vercel.com/blog/introducing-eve">Vercel&#8217;s Eve Is an Open-Source Framework for Building Serious Agents</a>: Eve brings durable execution, sandboxed compute, human-in-the-loop approvals, and evals to agent development. An agent is just a directory where each file describes one component, making the structure easy to read at a glance. Vercel-native by default, but fully portable with your own keys.</p><p>&#128679; <a href="https://x.com/sebastienlorber/status/2069670573555249498">React Compiler&#8217;s Rust Integration Gets Pulled Over Binary Size Bloat</a>: Integrating the React Compiler natively into Oxc was the right performance call, but the binary size ballooned enough to affect all Rolldown and Vite users. The integration has been temporarily pulled while the team works on slimming it down. Doing the right thing the hard way.</p><div><hr></div><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p>&#9876;&#65039; <a href="https://nx.dev/blog/meta-harnesses-agents-and-lessons-from-the-framework-wars">The Framework Wars Are Happening Again, This Time for AI Agents</a>: React beat batteries-included contenders by doing one thing well, and Next.js grew on top to supply the rest. The same pattern is playing out in AI agents: narrow agents will win, and a meta-harness layer will grow above them. A sharp read from the Nx team on where the agent ecosystem is headed.</p><p>&#128257; <a href="https://lucumr.pocoo.org/2026/6/23/the-coming-loop/">The Harness Loop: Where Autonomous Agents Work and Where They Don&#8217;t</a>: A harness-level loop sits outside the agent loop. Work goes into a queue, a machine picks it up, attempts it, and the harness decides whether to continue, inject another message, or start fresh with modified context. Great for porting code, performance explorations, and security scanning, but tends to produce overly defensive code when left entirely hands-off. A thoughtful, honest take from Armin Ronacher.</p><p>&#127807; <a href="https://github.blog/ai-and-ml/github-copilot/what-are-git-worktrees-and-why-should-i-use-them/">Git Worktrees: The 2015 Feature That AI Agents Just Made Relevant</a>: Git worktrees let you check out multiple branches simultaneously without stashing or switching context. They have been around for a decade but are suddenly essential now that AI coding agents need to work across branches in parallel. A solid explainer if you have never reached for this feature before.</p><p>&#129521; <a href="https://wasp.sh/blog/2026/06/22/javascript-still-cant-ship-a-full-stack-module">JavaScript Still Can&#8217;t Install a Full-Stack Module</a>: Rails and Laravel let you install a package that ships a full vertical slice: backend, frontend, and database model, all wired up and ready to go. JavaScript still can&#8217;t do this. It matters for AI agents too, since models work better with pre-built, well-tested modules that hide complexity behind a thin interface rather than hand-rolling everything from scratch. A well-argued case from the Wasp team on a gap the JS ecosystem hasn&#8217;t closed.</p><p>&#128202; <a href="https://00f.net/2026/06/23/webassembly-runtimes-2026/">WebAssembly Runtimes Benchmarked in 2026: Less Hype, More Numbers</a>: A benchmarking follow-up tracking whether WebAssembly runtimes have actually gotten faster year over year. Wasmer comes out as the best overall performer, with WAVM, WAMR, and Wasmtime close behind. The new wide_arithmetic instructions are proving to be a significant boost for crypto code on runtimes that support them. Exactly the kind of update the Wasm ecosystem needs. </p><div><hr></div><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p>&#129399; <a href="https://kage.tamnd.com/">Kage: Clone Any Website Into a Static, Offline Mirror</a>: Kage (&#24433;, "shadow") renders pages through headless Chrome, captures the fully assembled DOM, then strips out every script, event handler, and network call. CSS, images, and fonts are downloaded and rewritten to local paths so the offline copy looks identical to the live site. One command to clone, one to serve. A pure-Go binary with no dependencies and no fuss.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[#145: Smart Angular apps with Firebase AI Logic]]></title><description><![CDATA[By Aristeidis Bampakos]]></description><link>https://packtwebdevpro.substack.com/p/145-smart-angular-apps-with-firebase</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/145-smart-angular-apps-with-firebase</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 22 Jun 2026 14:02:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b6fa381c-9a0b-4ed0-b2ec-215c23902d68_200x200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><p style="text-align: center;"><strong>Meet the expert</strong></p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!eIZv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 424w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 848w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!eIZv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png" width="200" height="200" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:200,&quot;width&quot;:200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 424w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 848w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 1272w, /__u/substackcdn.com/image/fetch/$s_!eIZv!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0d1a8e3e-e92d-46a9-9a90-cc9a73d8d86a_200x200.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p><strong>Aristeidis Bampakos</strong> wrote this issue&#8217;s featured article on building smart Angular apps with Firebase AI Logic. He&#8217;s a Google Developer Expert (GDE) for Angular with 20+ years in software development. Based in Athens, he leads web development at Plex Earth, specializing in Angular. An award-winning author of <em><a href="https://www.amazon.com/Learning-Angular-practical-building-applications/dp/1835087485">Learning Angular</a></em> and <em><a href="https://www.amazon.com/Learning-Angular-practical-building-applications/dp/1835087485">Angular Projects</a></em>, he&#8217;s passionate about growing the developer community through writing, speaking, and teaching.</p></div><p>In this article, we will learn how to build smart and scalable Angular applications using the Firebase services from Google. We will see how to extend Angular applications by adding agentic AI capabilities using the Firebase AI Logic SDK.</p><p>The article is greatly inspired by my new book, <em><a href="https://www.amazon.com/Angular-Projects-building-real-world-enterprise-ebook/dp/B0GDRPDM1P/">Angular Projects &#8211; 4th edition</a></em>, which you can also purchase by scanning the QR code below:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ljpG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 424w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 848w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ljpG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png" width="171" height="171" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:215,&quot;width&quot;:215,&quot;resizeWidth&quot;:171,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The image depicts a QR code with a black QR code pattern overlaying a simple black and white checkerboard background.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The image depicts a QR code with a black QR code pattern overlaying a simple black and white checkerboard background.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="The image depicts a QR code with a black QR code pattern overlaying a simple black and white checkerboard background.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 424w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 848w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ljpG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c1db759-24cb-4a04-b43d-a13e5d5fab6d_215x215.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p style="text-align: center;"><em><sup><span>Figure 1: Book QR code</span></sup></em></p><p>Every chapter of the book is a separate real-world Angular project with modern technologies. Each project reflects how real teams work: forms, routing, data, and the small choices that make an application feel finished.</p><p>In one of the chapters, you will build <strong>CityPass Parking</strong>, an AI-enabled parking validator that allows users to submit car tickets and verify the time of their stay in the parking. The main feature of the project is a form to enter ticket details:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!E7f7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 424w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 848w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 1272w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!E7f7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png" width="522" height="176.37272727272727" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:223,&quot;width&quot;:660,&quot;resizeWidth&quot;:522,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;A digital form with fields for plate number, arrival, date selection, and location.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="A digital form with fields for plate number, arrival, date selection, and location.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="A digital form with fields for plate number, arrival, date selection, and location.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 424w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 848w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 1272w, /__u/substackcdn.com/image/fetch/$s_!E7f7!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9698b27f-309a-4ca1-8a4f-1aa1743cb137_660x223.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Figure 2: Ticket form</sup></em></p><p>The form consists of a date picker component and two input boxes that allow users to enter the required data.</p><h2>Business Logic in Angular</h2><p>The business logic layer in an Angular application defines how things are done in the application, and it is typically used for data access and complex tasks, such as:</p><ul><li><p>Accessing data from a REST API over HTTP</p></li><li><p>Interacting with the local storage of the browser</p></li><li><p>Performing data transformations and calculations</p></li></ul><p>Developers use Angular services to implement the business logic in an Angular application and make it available to components using the Angular <strong>Dependency Injection (DI)</strong> mechanism.</p><p>A popular best practice in the Angular community is that services should do one thing and do it well. The practice is based on the <strong>Single Responsibility Pattern (SRP)</strong>, and it is described in the official Angular styleguide <a href="https://angular.dev/style-guide#one-concept-per-file">here</a>. In the CityPass Parking application, we have a single service that creates a new parking ticket:</p><div class="callout-block" data-callout="true"><p><code>import { Service } from &#8216;@angular/core&#8217;;</code></p><p><code>@Service()</code></p><p><code>export class Tickets {</code></p><p><code>create(ticket: Ticket) {</code></p><p><code>}</code></p><p><code>}</code></p></div><p>When the business logic layer needs to support more use cases that cross domain boundaries, you must create a new service. We will see an example of this scenario in the following section.</p><h2>Adding new features</h2><p>The form of creating new tickets in the CityPass Parking application requires users to enter the location of the parking manually. According to the requirements, we could improve it further by allowing users to select the location from a list. The development team should make the following changes in the application:</p><ul><li><p>Change the UI of the specific component and add a select component for the available locations.</p></li><li><p>Create a new service to retrieve the locations and populate the select component:</p></li></ul><div class="callout-block" data-callout="true"><p><code>import { Service } from &#8216;@angular/core&#8217;;</code></p><p><code>@Service()</code></p><p><code>export class Locations {</code></p><p><code>getAll() {</code></p><p><code>}</code></p><p><code>}</code></p></div><p>The preceding steps require time and resources from the development team to implement the new feature. We could leverage the power of AI and support the new feature and future changes without ever touching the UI, using prompts.</p><p>We have already mentioned that Angular services should follow the one concept per file rule. One exception to this rule is when AI comes into the game because it can understand what it can do and when, with the right prompt. In the following section, we will see how AI can orchestrate the business logic of an Angular application and use prompts with Firebase AI Logic.</p><h2>Introducing Firebase AI Logic</h2><p>The Firebase AI Logic is a service of the Firebase product family that enables developers to add agentic AI capabilities in client-side applications:</p><ul><li><p>It supports a variety of programming languages, including JavaScript, which makes it appealing to Angular development.</p></li><li><p>It supports Gemini models and allows developers to interact with the Gemini products securely. Firebase AI Logic is connected with a Firebase project internally, so that no Gemini API key is required on the client side of the application.</p></li><li><p>It is the recommended approach when your existing infrastructure is based on Firebase because it integrates seamlessly with other Firebase services.</p></li></ul><p>In the following image, you can see a high-level overview of an AI agent based on the Firebase AI Logic, inside an Angular application:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!2ejA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 424w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 848w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!2ejA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png" width="536" height="353.27272727272725" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b393969-3450-491e-a3c3-79ce546a774e_660x435.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:660,&quot;resizeWidth&quot;:536,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The diagram illustrates a user interface for an Angular application, integrating Firebase AI SDK, Gemini model, and LLM (Large Language Model) instructions, with a sidebar featuring various tools.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The diagram illustrates a user interface for an Angular application, integrating Firebase AI SDK, Gemini model, and LLM (Large Language Model) instructions, with a sidebar featuring various tools.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="The diagram illustrates a user interface for an Angular application, integrating Firebase AI SDK, Gemini model, and LLM (Large Language Model) instructions, with a sidebar featuring various tools.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 424w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 848w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 1272w, /__u/substackcdn.com/image/fetch/$s_!2ejA!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b393969-3450-491e-a3c3-79ce546a774e_660x435.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Figure 3: Firebase AI Logic</sup></em></p><p>The yellow box resides on the server side, in Firebase services, and defines the Gemini model that the agent interacts with.</p><p>The blue boxes are the Angular application and the AI agent that reside on the client side. The Angular application uses the Firebase AI SDK, which is an npm package that exposes all the required methods for interacting with Firebase AI Logic, to create an agent. The LLM instructions describe the persona of the agent, and the tools define what the agent can do in the Angular application.</p><h2>Agent tools</h2><p>In the context of agentic AI, a tool is also known as function calling because it can call existing functions in an application. In the context of Angular, the agent communicates with the existing business logic layer to expose predefined methods as tools:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!nG9g!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 424w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 848w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!nG9g!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png" width="510" height="240.3181818181818" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:311,&quot;width&quot;:660,&quot;resizeWidth&quot;:510,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Al Agent.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Al Agent.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="Al Agent.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 424w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 848w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 1272w, /__u/substackcdn.com/image/fetch/$s_!nG9g!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffccf08d3-a043-4b6d-ae7e-a26245c4712f_660x311.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Figure 4: Agent tools</sup></em></p><p>As we see in the previous image, the AI agent of the CityPass Parking application exposes two tools: one for creating tickets and another for retrieving parking locations. The agent is also an Angular service that communicates with the <span>Tickets</span> and the <span>Locations</span> service classes to access their public API:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!OU8A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 424w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 848w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 1272w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!OU8A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png" width="508" height="256.3090909090909" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:333,&quot;width&quot;:660,&quot;resizeWidth&quot;:508,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The diagram illustrates a simple diagram with three interconnected nodes: Tickets, Locations, and Al Service.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The diagram illustrates a simple diagram with three interconnected nodes: Tickets, Locations, and Al Service.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="The diagram illustrates a simple diagram with three interconnected nodes: Tickets, Locations, and Al Service.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 424w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 848w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 1272w, /__u/substackcdn.com/image/fetch/$s_!OU8A!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2098d36b-8d4b-46ee-8fbd-6aeff076763f_660x333.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Figure 5: Business logic layer</sup></em></p><p>The tools of the agent are defined in the <code>functionDeclarations</code> property of a <code>FunctionDeclarationsTool </code>object:</p><div class="callout-block" data-callout="true"><p><code>const toolset: FunctionDeclarationsTool = {</code></p><p><code>functionDeclarations: [</code></p><p><code>{</code></p><p><code>name: &#8216;addTicket&#8217;,</code></p><p><code>description: &#8216;Add one ticket&#8217;,</code></p><p><code>parameters: Schema.object({</code></p><p><code>properties: {</code></p><p><code>plateNo: Schema.string(),</code></p><p><code>arrival: Schema.string(),</code></p><p><code>location: Schema.string()</code></p><p><code>}</code></p><p><code>})</code></p><p><code>}</code></p><p><code>]</code></p><p><code>};</code></p></div><p>In the above snippet, we define only one tool for creating parking tickets. A tool has the following properties:</p><ul><li><p><span>name</span>: The name of the tool. Agents respond with a suitable tool name according to the received prompt.</p></li><li><p><span>description</span> (optional): A short description.</p></li><li><p><span>parameters: </span>The properties needed by the agent to call the particular tool. Firebase AI Logic uses the Zod library to define a typed schema for the call parameters.</p></li></ul><p>The persona of the agent is a simple variable, such as the following:</p><div class="callout-block" data-callout="true"><p><code>const instructions = `</code></p><p><code>Welcome to CityPass.</code></p><p><code>You are a superstar agent for this car parking validator.</code></p><p><code>You will assist users by submitting parking tickets.</code></p><p><code>You can convert date phrases to ISO strings and</code></p><p><code>act as a geocode service to convert a location or address</code></p><p><code>to coordinates, long/lat.</code></p><p><code>`;</code></p></div><p>According to the previous snippet, the agent can convert dates to ISO strings and placemarks and addresses to coordinates.</p><p>We use the <code>getGenerativeModel </code>method from the <span>firebase/ai</span> package to combine the previous information with a Gemini model of our choice:</p><div class="callout-block" data-callout="true"><p><code>const ai = getAI(firebaseApp);</code></p><p><code>const model = getGenerativeModel(ai, {</code></p><p><code>model: &#8216;gemini-3-flash&#8217;,</code></p><p><code>systemInstruction: instructions,</code></p><p><code>tools: [toolset]</code></p><p><code>});</code></p></div><p>In the preceding snippet, the <span>ai</span> parameter comes from the configuration of a Firebase application that we have associated with the Firebase AI Logic through the Firebase Console.</p><p>The generated model can start listening for prompts using the following snippet:</p><div class="callout-block" data-callout="true"><p><code>const chat = model.startChat();</code></p></div><p>The <span>startChat</span> method initiates a chat session between the agent and any caller. The application forwards prompts to the Gemini model using the snippet below:</p><div class="callout-block" data-callout="true"><p><code>let result = await chat.sendMessage(prompt);</code></p></div><p>When the model returns a response, we can extract the selected tool and call the appropriate method from the service:</p><div class="callout-block" data-callout="true"><p><code>const calls = result.response.functionCalls();</code></p><p><code>if (calls &amp;&amp; calls[0].name === &#8216;addTicket&#8217;) {</code></p><p><code>const args = calls[0].args as Record&lt;string, string&gt;;</code></p><p><code>this.tickets.create(</code></p><p><code>args[&#8217;plateNo&#8217;],</code></p><p><code>args[&#8217;arrival&#8217;],</code></p><p><code>args[&#8217;location&#8217;]</code></p><p><code>);</code></p><p><code>}</code></p></div><p>The preceding snippet shows the execution of the tool for creating new parking tickets. The development team can now use the same agent to create a new tool for the retrieval of the available parking locations and expose it to the rest of the application. They will not need to modify the UI because users will be able to describe the new requirements with a prompt.</p><h2>Security considerations</h2><p>The Firebase AI Logic is a secure solution for adding agentic capabilities in a client-side application, as all interactions with the AI model go through Firebase services. Additionally, we can enhance the existing security measures with Firebase Server Templates. Firebase Server Templates move part of the agent to the server side, along with the Gemini models, and define them in a template:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!s-29!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 424w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 848w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 1272w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!s-29!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png" width="476" height="295.6969696969697" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:410,&quot;width&quot;:660,&quot;resizeWidth&quot;:476,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;The image depicts an architectural diagram illustrating components of an Angular application, including an LLM (Large Language Model), instructions, a Gemini model, Firebase AI SDK, and various tools.\n\n&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;.&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="The image depicts an architectural diagram illustrating components of an Angular application, including an LLM (Large Language Model), instructions, a Gemini model, Firebase AI SDK, and various tools.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." title="The image depicts an architectural diagram illustrating components of an Angular application, including an LLM (Large Language Model), instructions, a Gemini model, Firebase AI SDK, and various tools.

&#932;&#959; &#960;&#949;&#961;&#953;&#949;&#967;&#972;&#956;&#949;&#957;&#959; &#960;&#959;&#965; &#948;&#951;&#956;&#953;&#959;&#965;&#961;&#947;&#949;&#943;&#964;&#945;&#953; &#945;&#960;&#972; AI &#949;&#957;&#948;&#941;&#967;&#949;&#964;&#945;&#953; &#957;&#945; &#949;&#943;&#957;&#945;&#953; &#949;&#963;&#966;&#945;&#955;&#956;&#941;&#957;&#959;." srcset="/__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 424w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 848w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 1272w, /__u/substackcdn.com/image/fetch/$s_!s-29!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57780c52-e794-4eac-8d26-dd9e24f63f2f_660x410.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em><sup>Figure 6: Firebase Server Templates</sup></em></p><p>As shown in the previous image, the persona and the tool definition have been moved to the Firebase services. The described method has the following advantages:</p><ul><li><p>Prevents prompt injection.</p></li><li><p>Faster development iteration. Any change in the tool or the agent requires modifying the template.</p></li><li><p>Instant addition of new features. The server maintains multiple templates, and the application can switch to them using an ID.</p></li></ul><p>Firebase Server templates are a new feature in the Firebase AI Logic service that ensures security is at the forefront of any AI interaction.</p><h2>Conclusions</h2><p>The Firebase AI Logic is a secure and quick way to add agentic AI capabilities to an Angular application. The versatility of Firebase Server Templates makes it even more secure as it moves critical parts of the AI agent to the server. However, keep in mind that not everything in an application should be a prompt. Make a prompt for:</p><ul><li><p>Common tasks: Everyday tasks that your users do.</p></li><li><p>Easy tasks: Small tasks that need a small amount of information.</p></li><li><p>New features: Introduce AI gradually in your application.</p></li></ul><p>If you have the previous best practices in mind while designing AI experiences, you will build applications that your users will love to use!</p><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><p><strong><span>&#129302;</span> <a href="https://flueframework.com/blog/flue-1-0-beta/">Flue 1.0 Beta: Cloudflare&#8217;s Answer to the Agent Framework Wars</a></strong>: Not to be outdone by Vercel, Fred K. Schott and the Cloudflare crew dropped a beta for Flue, an open TypeScript-first agent framework with a hard stance against vendor lock-in. The headline addition is a clean split between autonomous Agents for open-ended tasks and deterministic Workflows for when you want exact control. Channels let you drop agents directly into Slack, GitHub, Linear, and more, and a durable event log means a crashed process picks right back up where it left off.</p><p><strong><span>&#128311;</span> <a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-rc">TypeScript 7.0 RC is Here, and It&#8217;s Written in Go</a></strong>: The TypeScript team has ported the entire codebase from TypeScript over to Go, and with native code speed and shared memory parallelism, TypeScript 7.0 is often about 10 times faster than TypeScript 6.0. The RC is available now via npm install -D typescript@rc. The catch: this version locks in 6.0&#8217;s stricter defaults as hard errors, so strict is on by default, target: es5 is gone, and moduleResolution: node is out the door. Worth reading the migration notes before you upgrade anything in prod.</p><p><strong><span>&#129514;</span> <a href="https://links.uk.defend.egress.com/Warning?crId=6a319dd1026102bc9ade0ea6&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNrLKCkpKLbS189KLEssTi7KLCgpT03NzqnUS87P1c_JzMvWN7QwMzU317cwT01NM7IwtbAAAOiNESw%3D&amp;@OriginalLink=javascriptweekly.com"><span>SvelteKit 3.0</span></a><span>, </span><a href="https://links.uk.defend.egress.com/Warning?crId=6a319dd1026102bc9ade0ea6&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNrLKCkpKLbS189KLEssTi7KLCgpT03NzqnUS87P1c_JzMvWN7QwMzW30LcwT01NM7IwtbAAAOiZES0%3D&amp;@OriginalLink=javascriptweekly.com"><span>Vue 3.6</span></a><span>, </span><a href="https://links.uk.defend.egress.com/Warning?crId=6a319dd1026102bc9ade0ea6&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNrLKCkpKLbS189KLEssTi7KLCgpT03NzqnUS87P1c_JzMvWN7QwMzW31LcwT01NM7IwtbAAAOilES4%3D&amp;@OriginalLink=javascriptweekly.com"><span>Vite 8.1</span></a><span>, and </span><a href="https://links.uk.defend.egress.com/Warning?crId=6a319dd1026102bc9ade0ea6&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNrLKCkpKLbS189KLEssTi7KLCgpT03NzqnUS87P1c_JzMvWN7QwM7Uw0LcwT01NM7IwtbAAAOhGESY%3D&amp;@OriginalLink=javascriptweekly.com"><span>Astro 7.0</span></a></strong><span> </span><strong>are all in beta</strong><br>A busy pre-release season for the JS ecosystem, four major framework updates are racing toward stable at once. If you&#8217;ve been waiting to see where the frontend tooling landscape lands before committing to a stack decision, now&#8217;s a good time to start poking at the betas.</p><p><strong><span>&#9881;&#65039;</span> <a href="https://bytecodealliance.org/articles/WASI-0.3">WASI 0.3 is officially here and async is now native to WebAssembly</a></strong><br>The WASI Subgroup voted to ratify WASI 0.3.0, rebasing WASI onto the WebAssembly Component Model&#8217;s async primitives. The big win: the host is now the one in charge of managing the one event loop shared by all components, which means components can finally be composed with each other without their event loops stepping on each other&#8217;s toes. For most microservices, this will reduce the time for calling other microservices from milliseconds to nanoseconds. </p><p><span>&#129504;</span> <strong><a href="https://applications.outskill.com/1426/20849/14672?data_id=a2a1b9d2-2067-426c-bf9e-69a0bddd95aa">2-Day Claude AI Mastery Workshop: Live This Weekend</a></strong></p><p>Claude has been shipping fast: Skills, Connectors, Cowork, vibe coding, and most people are still catching up. This weekend&#8217;s live workshop condenses 800+ hours of research and real-world practice into 16 focused hours covering Claude&#8217;s three modes, automation with Skills and Connectors, and 10+ AI tools and workflows. Saturday &amp; Sunday, 10 AM &#8211; 7 PM EST. <a href="https://applications.outskill.com/1426/20849/14672?data_id=a2a1b9d2-2067-426c-bf9e-69a0bddd95aa">Register here.</a></p><div><hr></div><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><p><strong><span>&#128184;</span> <a href="https://sunilpai.dev/posts/never-waste-a-token/">Never Waste a Token</a></strong><br>Sunil Pai digs into a problem that&#8217;s easy to miss until it shows up in your billing dashboard: when your process dies mid-inference, you don&#8217;t just lose your place, you lose money. The fix is a durable buffer sitting between your agent and the LLM provider, so a mid-stream deploy or crash doesn&#8217;t force a full retry. Resumable streaming and crash recovery turn out to be one mechanism, the same durable chunk log; the only question is whether a live producer is still attached. </p><p><strong><span>&#127760;</span> <a href="https://mohkohn.co.uk/writing/html-first/">How Building an HTML-First Site Doubled Users Overnight</a></strong><br>A real-world case study where ditching the React SPA in favour of server-rendered, progressively enhanced HTML wasn&#8217;t just a philosophical choice, it was the one that worked. When the site launched, the number of people completing the form doubled. The analytics team didn&#8217;t even know where these users were coming from, your JavaScript-based analytics package doesn&#8217;t see the users you&#8217;re bouncing because of JavaScript failures. </p><p><strong><span>&#128065;&#65039;</span> <a href="https://vizzly.dev/blog/smart-ignore-regions-visual-testing-that-learns-without-looking-away/">Smart Ignore Regions: Visual Testing That Learns Without Looking Away</a></strong><br>The classic pain of visual regression testing: dynamic content like timestamps and footer copy triggers false positives on every build, so you draw a box around it and move on, except now you&#8217;ve created a blind spot. Vizzly&#8217;s smart ignore regions use Honeydiff&#8217;s change metadata to find recurring dynamic areas, ask you to confirm whether the change is expected, and use that decision on future reviews without turning the whole area into a blind spot. In their own dogfooding, confirming one footer region took 41 changed screenshots out of the review queue without changing the test suite. </p><p><strong><span>&#129302;</span> <a href="https://yes-we-scan.app/details">Building a WASM-Powered Scanner Driver in the Browser</a></strong><br>A fun one: a dev resurrected an old USB scanner with no modern driver support by running a WASM-powered emulator in the browser, bridged to the physical hardware via WebUSB. Niche, sure, but it&#8217;s a great illustration of how far browser APIs have come when you actually push them.</p><p><strong><span>&#127912;</span> <a href="https://polar.sh/blog/orbit-llm-safe-design-system">Orbit: Polar&#8217;s LLM-Safe Design System</a></strong><br>The problem isn&#8217;t that LLMs can&#8217;t write CSS or Tailwind, they write it fluently. The problem is that they write it without being aware of the underlying decisions. Polar&#8217;s new Orbit system bets on making off-brand choices structurally impossible: if a value isn&#8217;t a design decision they&#8217;ve actually made, it should not pass CI. Intent-named tokens, typed props over raw class strings, and ESLint rules that enforce it all. </p><div><hr></div><h1 style="text-align: center;"><strong>Developer Toolbox</strong></h1><p><strong><span>&#127922;</span> <a href="https://polycss.com">PolyCSS</a></strong><br>PolyCSS renders solid and textured 3D meshes in the DOM without WebGL &#8212; each polygon is a real element you can style, click, and inspect. It supports OBJ, glTF, GLB, and MagicaVoxel VOX files and works with vanilla JS, React, and Vue. Mostly a curiosity, but the kind that makes you want to open the builder just to see how far you can push it.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://links.uk.defend.egress.com/Warning?crId=6a300588c3471930c7653495&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwlEKgCAMANDT9OsgCKPPoD7rDGMYwbSpW0iePt67zbIuABmJzUkKFNmRJMhSDSM0U3jDMG30HOPVGUuvhfy5Y_pmXT3iDzBGGBI%3D&amp;@OriginalLink=packt.omeclk.com">Advertise with us</a></strong><br><strong> </strong><span>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</span></p></div>]]></content:encoded></item><item><title><![CDATA[WebDevPro #144: Is Your Service Architecture Working For You or Against You? ]]></title><description><![CDATA[The debate around microservices never really goes away.]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-144-is-your-service-architecture</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-144-is-your-service-architecture</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 15 Jun 2026 14:02:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!bBmM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The debate around microservices never really goes away. You will find equal conviction on both sides: engineers who swear by them and those who have spent years untangling the mess they can become. The truth, as it usually is in software architecture, sits somewhere in the uncomfortable middle, and understanding that middle space is what separates developers who pick architectures thoughtfully from those who follow the current consensus without questioning it.</p><p>Microservices promised a clean world. Small, isolated services, each doing one thing, each deployable on its own. In theory, a developer can work on a single service, test it in isolation, and ship it without needing to understand the rest of the system. For teams burned by monolithic codebases that had grown into unmaintainable sprawl, the appeal was real.</p><p>But the promise comes with costs that compound as a system grows, and most architecture discussions gloss over them until a team is already deep in the weeds.</p><p>Before we dig deeper into this, here&#8217;s a TL;DR you&#8217;ll need:</p><ul><li><p>&#127912; <em><strong><a href="https://nodered.org/blog/2026/06/09/version-5-0-released">Node-RED 5.0: the biggest editor overhaul in the project&#8217;s history</a></strong></em></p></li><li><p>&#128396;&#65039; <em><strong><a href="https://blog.janestreet.com/i-design-with-claude-code-more-than-figma-now-index/">A Jane Street designer now prototypes in Claude Code more than Figma</a></strong></em></p></li><li><p>&#128221; <em><strong><a href="https://mohkohn.co.uk/writing/html-first/">How building an HTML-first site doubled a company&#8217;s users overnight</a></strong></em></p></li><li><p>&#128197; <em><strong><a href="https://nodejsdesignpatterns.com/blog/nodejs-release-schedule-changes/">Node.js is finally fixing its confusing release schedule</a></strong></em></p></li><li><p>&#129695; <em><strong><a href="https://aave.com/design/building-glass-for-the-web">How Aave built a Liquid Glass component library for the web</a></strong></em></p></li><li><p>&#128126; <em><strong><a href="https://www.raymondcamden.com/2026/06/02/ai-versus-a-grue">Can Chrome&#8217;s built-in AI play Zork? Not really</a></strong></em></p></li></ul><h2><strong>The Docker Build Loop Nobody Warned You About</strong></h2><p>Consider what happens when you begin with a straightforward to-do service and an auth service. Two services, manageable. But product requirements evolve, as they always do. You need email notifications when users are added to groups. You need rate limiting on registration to prevent email flooding. You need organizational structures with departments, permissions, and roles. Suddenly you have five services, each with its own database, each with dependencies on at least two others.</p><p>In theory, microservices are meant to be independent. In practice, real production systems rarely achieve this. Services end up needing data from each other constantly, and each new feature decision pulls at those dependencies further. The dream of true service isolation runs into the reality of shared business logic.</p><p>The first place this becomes painful is local development. When you are building a feature that touches the org service, the auth service, and the to-do service simultaneously, you are no longer working on one small service. You are building across three repos, running multiple Docker containers, waiting on separate builds, and debugging the network glue between them every time something breaks. For a small team moving fast, this overhead does not scale the way the architecture diagram suggested it would.</p><p>There is a real pattern here: teams start with microservices because the architecture looks clean on a whiteboard, then spend a disproportionate amount of their engineering time not building features, but managing the plumbing between services.</p><h2><strong>When Your Services Have No Idea What the Others Are Doing</strong></h2><p>There is a subtler problem that compounds the above, and it is harder to see until a system reaches a certain size. In a microservices architecture, no single service has full visibility into what is happening across the system. Each service operates in its own process, in its own memory space, talking to others only through network calls.</p><p>This creates a condition that is easy to underestimate: services end up duplicating checks on each other&#8217;s work because no service can fully trust what any other service has already done.</p><p>A classic example is auth verification. A developer working on the org service cannot know with certainty that the upstream to-do service has already verified the user&#8217;s role. So they add their own auth check. Then a project management service is built later, and it does the same. Eventually, a single user action triggers multiple redundant calls to the same auth service, a phenomenon that plays out at enormous scale in large systems.</p><p>DoorDash documented a version of this internally, where a single order placement triggered roughly 300 calls to the account service, precisely because all downstream services independently wanted to verify account state.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!9W0U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 424w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 848w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!9W0U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png" width="624" height="157" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:157,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;DNS returns both IPs&quot;,&quot;title&quot;:&quot;DNS returns both IPs&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="DNS returns both IPs" title="DNS returns both IPs" srcset="/__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 424w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 848w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 1272w, /__u/substackcdn.com/image/fetch/$s_!9W0U!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F10b7740a-7add-4fbe-82c2-1ad4363d0bf6_624x157.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Beyond the redundant calls, there is the problem of circular dependencies. In a modular codebase, the compiler catches a circular dependency at build time and refuses to proceed. In a microservices architecture, those circular dependencies manifest at runtime as network calls, completely invisible at compile time. By the time you discover the problem, you may be debugging a cascading failure in production</p><h2><strong>What Nanoservices Actually Get Right</strong></h2><p>The nanoservice model, applied in production on Kubernetes clusters, attempts to preserve the isolation and modularity benefits of microservices while cutting out the operational costs that make them painful at small-to-medium scale.</p><p>The core insight is architectural rather than infrastructure-level. Instead of splitting services at the process boundary, with each service in its own Docker container communicating exclusively over the network, nanoservices split at the code module boundary, organized into layers: a networking layer, a core logic layer, and a data access layer. Each layer is independently testable and independently swappable, but they are compiled into a single binary.</p><p>This matters in a few concrete ways. Builds are faster because you are not waiting for multiple Docker builds and inter-service network configuration to line up. Circular dependencies become compiler errors rather than runtime surprises. When one core function needs data from another service, that data can be passed directly in memory rather than serialized, sent over a network, deserialized, and returned. The performance difference is not marginal.</p><p>Critically, this does not close off the option of deploying as microservices later. If a particular service grows large enough to warrant independent deployment, maybe it has distinct scaling requirements or a distinct team working on it, it can be extracted. The code is already structured to make that transition clean. You are not locked in either direction.</p><h2><strong>Asking a Better Question Than Monolith vs. Microservices</strong></h2><p>The monolith-vs-microservices question is usually the wrong question to lead with. The more useful question is: what granularity of modularization serves the team and product at this stage of the system&#8217;s life?</p><p>A small team building quickly should probably start with something closer to a nanoservice structure: clean module separation, well-defined layers, everything compiled together. This gives the developer experience of working on isolated, understandable pieces without the operational weight of managing multiple networked services. If the system grows and a genuine case emerges for pulling out a service, the modular structure makes that extraction tractable.</p><p>The teams that regret their architecture decisions are usually the ones that adopted microservices as an upfront commitment rather than as an option they might grow into. They took on the full operational complexity of the architecture before the system warranted it.</p><p>There is a related cost worth naming: microservices architectures are expensive to observe. Without investment in centralized logging, distributed tracing, and monitoring across all services, debugging becomes genuinely difficult. A network call fails somewhere in a chain of five services, and identifying where and why becomes a non-trivial investigation. Observability infrastructure is often an afterthought until the pain of not having it becomes too great to ignore.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!clLh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 424w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 848w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 1272w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!clLh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png" width="624" height="290" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:290,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 424w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 848w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 1272w, /__u/substackcdn.com/image/fetch/$s_!clLh!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fca10e0a5-d8a1-4997-a0a0-5d812751093e_624x290.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Why Rust Makes This Argument Stronger</strong></h2><p>Most architecture discussions about microservices are language-agnostic, but they should not be entirely. Rust brings something specific to this conversation: the compiler as a collaborator rather than just a validator. When services are compiled together into a single binary, Rust&#8217;s type system and borrow checker enforce contracts between modules at build time. A bad interface, a circular dependency, an incorrect assumption about ownership, these surface as compile errors before they become production incidents.</p><p>This is a qualitatively different experience from working across service boundaries in dynamically typed languages, where the contract between services is enforced only at runtime, usually through documentation, convention, and hope. Rust makes the case for compiling services together stronger than it would be in a language with weaker static guarantees.</p><p>There is also the performance dimension. The latency cost of an in-process function call versus a network call is not trivial, especially as request complexity grows and the number of inter-service calls per request increases. Compiling services into a single binary eliminates that latency for cross-module communication, while Rust&#8217;s zero-cost abstractions ensure you are not paying hidden runtime costs elsewhere to achieve it.</p><h2><strong>Production-Readiness Is a Design Decision, Not a Final Step</strong></h2><p>One pattern worth internalizing is that production-readiness is not a state you arrive at after building; it is a set of decisions baked into how you structure code from the beginning. This means thinking about error handling across module boundaries early, building your logging and observability layer before you need to debug a production issue, and testing not just individual units but the interactions between layers.</p><p>The architecture you choose shapes how tractable all of this is. Clean module separation with explicit data access layers makes unit testing far more manageable because you can mock dependencies at the boundary. End-to-end atomic tests become more reliable when you are not introducing network uncertainty into every interaction. Deployment pipelines simplify when you are shipping one binary rather than coordinating releases across a constellation of services.</p><p>None of this is an argument against microservices in general. At sufficient scale, with sufficient team size and operational maturity, microservices solve real coordination problems. But for most web developers working on most systems at most stages of a product&#8217;s life, the more honest architecture discussion starts with modular code that compiles together, and evaluates the case for network boundaries only when a concrete, specific need for them emerges.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!BqV8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 424w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 848w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!BqV8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png" width="624" height="347" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3742e5de-a067-4132-b249-39378a36876e_624x347.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:347,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 424w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 848w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BqV8!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3742e5de-a067-4132-b249-39378a36876e_624x347.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Speaking of building production-ready applications in Rust</strong>, we are running a live workshop with <em>Francesco Ciulla</em> where you will work through exactly these kinds of real-world engineering decisions. Using Claude and Codex as AI-native workflow tools, you will scaffold, debug, refactor, test, and ship a practical Rust application live. The workshop covers backend setup, AI integrations, retrieval and chat systems, and production-ready patterns; the kind of hands-on work that makes these architectural concepts concrete.</p><p><em><strong><a href="https://www.eventbrite.co.uk/e/build-production-ready-ai-applications-with-rust-claude-and-codex-tickets-1987053747248?aff=newsletterml&amp;DISCOUNT=SAVE35">Build Production-Ready AI Applications with Rust, Claude, and Codex</a></strong></em><strong>.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!bBmM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 424w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 848w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!bBmM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png" width="940" height="470" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:470,&quot;width&quot;:940,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 424w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 848w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 1272w, /__u/substackcdn.com/image/fetch/$s_!bBmM!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e3aa487-6ab4-4b23-bd17-aabcd74ff6b8_940x470.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/build-production-ready-ai-applications-with-rust-claude-and-codex-tickets-1987053747248?aff=newsletterml&amp;DISCOUNT=SAVE35">Claim Your Discounted Seat</a></strong></p><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p>&#128680; <strong><a href="https://www.bbc.com/news/articles/c932g3v3e13o">Anthropic pulls its newest AI model after the US government flags security concerns</a></strong>: Days after launch, Anthropic was ordered to disable Claude Fable 5 and Mythos 5 for all users over a possible jailbreak, letting it exploit cyber defences. The UK&#8217;s AI Security Institute reportedly found the model could breach systems 73% of the time in testing. This one&#8217;s worth watching closely, it&#8217;s tangled up with an ongoing Anthropic vs Trump administration lawsuit and raises real questions about how &#8220;too powerful to release&#8221; models get governed going forward.</p></li><li><p>&#127912; <strong><a href="https://nodered.org/blog/2026/06/09/version-5-0-released">Node-RED 5.0: the biggest editor overhaul in the project&#8217;s history</a></strong>:<a href="https://nodered.org/blog/2026/06/09/version-5-0-released"> </a>A built-in dark theme, split sidebars, pausable debug output, and Function nodes that can now call Link nodes and wait on a response. If you&#8217;ve been running Node-RED on autopilot for years, this release is the nudge to finally poke around the editor again. Just know it now demands Node.js 22.9+ minimum.</p></li><li><p>&#129695; <strong><a href="https://aave.com/design/building-glass-for-the-web">How Aave built a Liquid Glass component library for the web</a></strong>: Timed neatly with WWDC, Lochie Axon walks through recreating Apple&#8217;s Liquid Glass aesthetic as a cross-browser component library. Worth a look if you&#8217;ve been itching to bring that frosted, refractive UI trend into your own web projects without waiting on native support.</p></li><li><p>&#128197; <strong><a href="https://nodejsdesignpatterns.com/blog/nodejs-release-schedule-changes/">Node.js is finally fixing its confusing release schedule</a></strong>: Starting October 2026, Node ditches the odd/even LTS dance for one major release a year, with version numbers matching the calendar year (27 in 2027, and so on). Every release becomes LTS, plus a new Alpha channel gives library maintainers a six month head start on breaking changes. If you only run LTS, this barely changes your life, which is exactly the point.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p>&#128221; <strong><a href="https://mohkohn.co.uk/writing/html-first/">How building an HTML-first site doubled a company&#8217;s users overnight</a></strong>: A genuinely great read on rebuilding a regulated utility&#8217;s broken React form using Astro, server redirects, and a tiny validation web component, no client-side state management in sight. The user numbers doubled the moment it shipped, and the takeaway about who gets bounced by heavy JS is one every product team should sit with.</p></li><li><p>&#129513; <strong><a href="https://norswap.com/typescript-type-variable-inference/">How TypeScript actually infers type variables</a></strong>: If generic inference has ever surprised you in a way that felt like TypeScript was just messing with you, this breaks down the candidate collection and resolution process in painful detail. Dense, but it&#8217;s the kind of dense that actually resolves years of &#8220;why does TS do that&#8221; confusion.</p></li><li><p>&#128126; <strong><a href="https://www.raymondcamden.com/2026/06/02/ai-versus-a-grue">Can Chrome&#8217;s built-in AI play Zork? Not really</a></strong>: Raymond Camden hooks Chrome&#8217;s on-device Prompt API up to a JS port of Zork 1 and watches it wander in circles examining the same pile of leaves. A fun, low-stakes experiment that&#8217;s also a decent gut check on how far local, lightweight models actually get with multi-step reasoning.</p></li><li><p>&#128396;&#65039; <strong><a href="https://blog.janestreet.com/i-design-with-claude-code-more-than-figma-now-index/">A Jane Street designer now prototypes in Claude Code more than Figma</a></strong>: Edwin Morris describes skipping mockups entirely and shipping working prototypes, including 2000+ line diffs, directly with Claude. The honest bit about what this does to code review and the &#8220;disposable prototype&#8221; framing is the part worth chewing on, not just the productivity win.</p></li><li><p>&#129482; <strong><a href="https://github.com/mrdoob/draco.js">Draco.js: a pure JS mesh loader for three.js, by mrdoob himself</a></strong>: A from-scratch Draco decoder with no WASM dependency, plus a <strong><a href="https://mrdoob.github.io/draco.js/">live demo</a></strong> to poke at. If your three.js pipeline has been fighting with Draco&#8217;s official loader, this is worth bookmarking.</p></li></ul><h1 style="text-align: center;"><strong>The Developer Toolbox</strong></h1><p>&#128196; <strong><a href="https://www.extend.ai/ui">Extend UI: open source React components for document apps</a></strong><br>PDF, DOCX, XLSX, and CSV viewers with bounding box citations, file upload, and e-signing, all ready to drop into agents or internal tools. If you&#8217;re tired of rebuilding document viewers from scratch every time a client wants &#8220;AI that reads my files,&#8221; this saves a real chunk of work.</p><p>&#128231; <strong><a href="https://github.com/Kikobeats/free-email-domains">free-email-domains: a maintained list of every free email provider</a></strong><br>Kiko has compiled and actively maintains a list of free email domains, perfect for screening signups and flagging spammy or throwaway addresses. Small utility, big quiet usefulness.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="callout-block" data-callout="true"><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p></div>]]></content:encoded></item><item><title><![CDATA[WebDevPro #143: Why Your Microservices Need More Than Round-Robin DNS]]></title><description><![CDATA[Meet the author]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-143-why-your-microservices</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-143-why-your-microservices</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 08 Jun 2026 14:02:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/894b635a-a7fc-4fbe-be76-a4b1627a6662_207x207.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong>Meet the author</strong></h1><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sOc4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sOc4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg" width="207" height="207" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:207,&quot;width&quot;:207,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!sOc4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5502572-6052-4674-a084-ae1f6007c8b4_207x207.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p style="text-align: center;"><sup>This article draws on insights from </sup><strong><sup>Magnus Larsson</sup></strong><sup>, an IT industry veteran who has worked in the field since 1986 and has consulted for major Swedish companies such as Volvo, Ericsson, and AstraZeneca. Earlier in his career, Magnus experienced firsthand the challenges of building distributed systems. Today, many of those challenges can be addressed with open-source tools such as Spring Cloud, Kubernetes, and Istio. Over the past eight years, he has helped customers adopt these technologies and shared his expertise through presentations and blog posts.</sup></p></div><div><hr></div><p>There&#8217;s a moment in almost every distributed systems project where someone asks a completely reasonable question: <em>why can&#8217;t we just use DNS?</em> Every instance of a service registers under the same hostname, the DNS server hands back a list of IPs, clients cycle through them. It sounds elegant. It sounds like a solved problem. And for a while, especially in early-stage systems, it kind of works.</p><p>Then you scale up. Instances start crashing and restarting. Network partitions happen. You add a health check somewhere and realize DNS has no idea whether the IP it just returned belongs to a process that&#8217;s been dead for forty seconds. The elegant solution starts showing its seams.</p><p>This article is about why DNS-based service discovery breaks down in distributed systems, what the failure modes actually look like in practice, and how client-side discovery handles the chaos that DNS was never designed for.</p><p>Before we dig deeper into this, here&#8217;s a TL;DR you&#8217;ll need:</p><ul><li><p><em><strong>&#128314; <a href="https://blog.angular.dev/announcing-angular-v22-c52bb83a4664">Angular v22 is Here</a></strong></em></p></li><li><p><em><strong>&#9889; <a href="https://blog.cloudflare.com/voidzero-joins-cloudflare/">VoidZero Joins Cloudflare</a></strong></em></p></li><li><p><strong>&#128736;&#65039; </strong><em><strong><a href="https://github.blog/changelog/2026-06-02-copilot-sdk-is-now-generally-available/">Copilot SDK is Now GA</a></strong></em></p></li><li><p><strong>&#128216; </strong><em><strong><a href="https://github.com/AllThingsSmitty/typescript-tips-everyone-should-know">TypeScript Tips Everyone Should Know</a></strong></em></p></li><li><p><strong>&#127916; </strong><em><strong><a href="https://www.joshwcomeau.com/animation/css-vs-javascript/">CSS vs. JavaScript Animations</a></strong></em></p></li></ul><h2><strong>The DNS Round-Robin Promise</strong></h2><p>The idea behind round-robin DNS is straightforward. Multiple service instances register under the same DNS name. When a client resolves that name, the DNS server returns a list of IP addresses, one per instance, and the client works through them sequentially. The first request goes to the first IP, the second to the next, and so on. Load is distributed. Everyone goes home happy.</p><p>The problem is that this model is built on an assumption that quietly breaks in dynamic environments: that the list of IP addresses returned by DNS accurately reflects the set of healthy, reachable instances <em>right now</em>.</p><p>In a microservices environment, that assumption fails constantly.</p><h2><strong>What Actually Happens When You Try It</strong></h2><p>To make this concrete, consider a scenario where you scale a service to two instances and ask a dependent service what IPs it sees. DNS returns both. But when you start sending real traffic, you notice something strange: requests keep going to the same instance. The load balancing you expected isn&#8217;t happening.</p><p>This reveals one of DNS&#8217;s fundamental limitations as a load balancer: DNS clients don&#8217;t do per-request round-robin. They ask a DNS server once, get a list, try the first address that works, and then <em>hold onto it</em>. Caching is baked into how DNS works. It&#8217;s a feature for performance, but a liability when you need live instance awareness. Once a client finds a working address, it stops asking.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!3rK7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 424w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 848w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!3rK7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png" width="624" height="295" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:295,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;DNS returns both IPs&quot;,&quot;title&quot;:&quot;DNS returns both IPs&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="DNS returns both IPs" title="DNS returns both IPs" srcset="/__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 424w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 848w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 1272w, /__u/substackcdn.com/image/fetch/$s_!3rK7!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2317514b-e846-4ca2-aad3-6e960f2113e6_624x295.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That&#8217;s problem one. Problem two is what happens when an instance disappears.</p><h2><strong>The Staleness Problem</strong></h2><p>DNS records have a time-to-live (TTL), and until that TTL expires, clients are working from a cached snapshot of the world. If an instance crashes, DNS doesn&#8217;t know. If a new instance starts up, DNS doesn&#8217;t know that either, not until someone updates the record and the TTL rolls over. In a system where instances come and go every few minutes, even a 30-second TTL creates meaningful windows of failure.</p><p>More critically, DNS has no concept of health. An IP address in a DNS response is just an IP address. It carries no information about whether the process listening there is actually ready to handle requests, whether it&#8217;s mid-restart, or whether it&#8217;s responding to health checks but silently dropping traffic due to a downstream issue. DNS cannot answer the question &#8220;is this instance okay?&#8221; because it was never designed to.</p><h2><strong>The Challenges DNS Can&#8217;t Address</strong></h2><p>When you enumerate what a robust service discovery mechanism actually needs to handle, the gap becomes clear.</p><p>New instances can appear at any time and need to be made available to clients quickly, not after a TTL expires. Existing instances can fail at any time, and failed instances need to be removed from rotation just as quickly. Some instances that fail temporarily might recover and should rejoin rotation; others won&#8217;t and should be permanently deregistered. New instances often have startup time as well. They can accept TCP connections before they&#8217;re actually ready to serve traffic, which means &#8220;is the port open&#8221; is a poor proxy for &#8220;is this instance ready.&#8221; And unintended network partitions, where a client loses connectivity to some instances but not others, can cause cascading failures if the discovery layer doesn&#8217;t account for them.</p><p>None of these are edge cases. They&#8217;re the normal operating conditions of a distributed system at any meaningful scale.</p><h2><strong>Client-Side Discovery: A Different Model</strong></h2><p>The approach taken by systems like Netflix Eureka flips the model. Rather than relying on DNS as a passive lookup table, it introduces an active registry that instances communicate with continuously.</p><p>When a service instance starts, it registers itself with the discovery server, not just its address but information about what it is. On a regular interval, it sends a heartbeat to signal that it&#8217;s still alive and healthy. If the heartbeats stop, the discovery server removes the instance from the registry after a configurable window. Clients, meanwhile, periodically fetch the current registry from the discovery server and cache it locally. When a client needs to make a call, it already has a fresh list of available instances and can select one without making a synchronous request to the discovery service for every call.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!8iLP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 424w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 848w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!8iLP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png" width="624" height="312" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:312,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 424w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 848w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 1272w, /__u/substackcdn.com/image/fetch/$s_!8iLP!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc3495966-e538-42ee-bdc9-f218777b7ebb_624x312.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This architecture gets several things right that DNS doesn&#8217;t.</p><ul><li><p><strong>Instance registration is active, not passive.</strong> A service has to deliberately register itself, and it has to keep proving it&#8217;s alive through heartbeats. An instance that crashes stops sending heartbeats and gets removed from the registry. There&#8217;s no waiting for a TTL.</p></li><li><p><strong>Readiness is separable from availability.</strong> A service can control when it registers itself, after initialization is complete, after database connections are established, after whatever startup work needs to happen. The registry reflects intent, not just the existence of a listening port.</p></li><li><p><strong>Clients are participants, not observers.</strong> Because clients cache the registry locally and refresh it on a schedule, they can make load-balancing decisions with reasonably current information, and they do so per-request rather than per-connection. This is what produces actual round-robin behavior in practice.</p></li><li><p><strong>The system degrades gracefully.</strong> If the discovery server itself goes down, clients continue operating from their local cache. They can still reach instances that were registered before the outage. New instances can&#8217;t register and deregistered instances won&#8217;t be removed, but existing traffic keeps flowing. If a DNS server goes down, resolution fails entirely.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!sZ74!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 424w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 848w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!sZ74!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png" width="624" height="260" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:260,&quot;width&quot;:624,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 424w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 848w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 1272w, /__u/substackcdn.com/image/fetch/$s_!sZ74!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffece6e7f-8864-44eb-85a5-b251d70ac8d8_624x260.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>The Propagation Question</strong></h2><p>One nuance worth understanding is that client-side discovery still has propagation delay. It&#8217;s just controlled and predictable rather than dependent on TTLs set by infrastructure you may not own.</p><p>When an instance spins up and registers, clients that have already fetched the registry won&#8217;t know about it until their next refresh cycle. Similarly, when an instance goes down, there&#8217;s a window between the last heartbeat and the next client cache refresh during which they might try to call an address that no longer works. This is why production systems built on client-side discovery pair it with retry logic and circuit breakers. The discovery layer reduces the failure surface significantly, but it doesn&#8217;t eliminate the need for resilience patterns at the call level.</p><p>The important difference from DNS is that these windows are tunable. In a development environment, you might configure clients to refresh every five seconds and instances to send heartbeats just as frequently. In production, you&#8217;d balance freshness against the load of constant registry polling. DNS gives you no such control.</p><h2><strong>What This Means in Practice</strong></h2><p>The practical implication of all this is that service discovery isn&#8217;t primarily a load balancing problem. It&#8217;s a <em>membership</em> problem. The question being answered is &#8220;who is currently in this service&#8217;s pool of healthy instances?&#8221; and DNS was built for a world where membership changes on a timescale of hours or days, not seconds or minutes.</p><p>Client-side discovery systems treat membership as a live, continuously updated data structure. Instances opt in by registering and staying registered through heartbeats. Clients subscribe to changes in that membership by periodically refreshing their local view. The discovery server is the source of truth, but it&#8217;s a source of truth that expects the world to change constantly and is designed accordingly.</p><p>When you start thinking about service discovery through this lens, as a membership system rather than a name resolution system, the limitations of DNS become obvious. DNS is a remarkably well-engineered solution to the problem it was designed to solve. Service discovery in distributed systems is a different problem entirely.</p><h2><strong>Takeaways</strong></h2><ul><li><p><strong>DNS round-robin doesn&#8217;t actually round-robin.</strong> Clients cache working addresses and stick with them. The per-request load distribution you expect doesn&#8217;t happen in practice.</p></li><li><p><strong>DNS has no health awareness.</strong> An IP in a DNS response carries no signal about whether that instance is alive, ready, or functional. Health and availability are entirely separate concerns that DNS has no mechanism to represent.</p></li><li><p><strong>Instance registration needs to be active, not passive.</strong> Robust service discovery requires instances to continuously prove they&#8217;re alive, not just appear in a record once. Heartbeat-based registration is the mechanism that makes deregistration automatic and timely.</p></li><li><p><strong>Propagation delay exists in all discovery systems, but client-side discovery makes it controllable.</strong> Understanding the refresh window and pairing discovery with retry logic is the path to resilient service-to-service communication.</p></li><li><p><strong>The discovery server is not a single point of failure if clients cache.</strong> Because clients maintain local copies of the registry, discovery server downtime degrades gracefully rather than catastrophically, a significant practical advantage over centralized DNS in high-availability requirements.</p></li></ul><p>Service discovery isn&#8217;t glamorous infrastructure. It&#8217;s the kind of thing that works invisibly when it&#8217;s right and causes deeply confusing failures when it&#8217;s wrong. Getting the model right from the start saves a lot of painful debugging later.</p><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p><strong>&#9889; <a href="https://blog.cloudflare.com/voidzero-joins-cloudflare/">VoidZero Joins Cloudflare</a></strong>: Big news: Cloudflare has acquired VoidZero, the team behind Vite, Vitest, Rolldown, and Oxc, and has pledged $1 million to an independent Vite ecosystem fund. Evan You was frank about the reason: monetizing open-source tooling has proven extremely hard, despite Vite&#8217;s enormous adoption. Vite stays MIT-licensed and vendor-neutral, but Cloudflare now has its hands on the plumbing of a large chunk of the modern web. Worth watching closely.</p></li><li><p><strong>&#128314; <a href="https://blog.angular.dev/announcing-angular-v22-c52bb83a4664">Angular v22 is Here</a></strong>: The signal-first era is no longer a roadmap promise. Signal Forms and resources are now stable, OnPush is now the default change detection strategy, and the HTTP client uses Fetch by default. This is a consolidation release: the experiments are over, and you&#8217;re looking at the Angular team&#8217;s considered view of how production apps should be built in 2026. If you&#8217;ve been waiting for the right time to migrate, that time is now.</p></li><li><p><strong>&#9760;&#65039; <a href="https://arstechnica.com/security/2026/06/dozens-of-red-hat-packages-backdoored-through-its-offical-npm-channel/">Red Hat&#8217;s npm Namespace Got Backdoored</a></strong>: A compromised Red Hat employee GitHub account was used to inject malicious workflows into three RedHatInsights repositories, with OIDC tokens publishing backdoored package versions that carried valid SLSA provenance attestations, making them look completely legitimate. The worm self-propagates using stolen npm tokens, bypassing 2FA to republish backdoored versions of other packages autonomously. This one is nasty. If you ran npm install on anything under @redhat-cloud-services after June 1st, rotate everything.</p></li><li><p><strong>&#129505; <a href="https://svelte.dev/blog/whats-new-in-svelte-june-2026">What&#8217;s New in Svelte: June 2026</a></strong>: This month brings better forms, new long-lived remote query APIs, and TypeScript 6 support in language-tools. The standout addition is .live(...), a new query function that makes pulling real-time server data dramatically cleaner. Svelte&#8217;s changelog keeps getting better without the drama. Quiet excellence.</p></li><li><p><strong>&#129302; <a href="https://2026.stateofai.dev/en-US">State of AI 2026</a></strong>: Devographics surveyed 7,258 developers on AI in their workflows, and the numbers are striking. The average proportion of AI-generated code has jumped from 28% in 2025 to 54% this year, with the 75%+ segments seeing the highest growth. ChatGPT leads in raw usage, but Claude tops the charts for positive sentiment and is the model developers are most willing to actually pay for. The full dataset is worth digging into; pull up the interactive charts.</p></li><li><p><strong>&#128736;&#65039; <a href="https://github.blog/changelog/2026-06-02-copilot-sdk-is-now-generally-available/">Copilot SDK is Now GA</a></strong>: You can now embed GitHub Copilot&#8217;s agentic engine directly into your own applications and developer tools, with access to planning, tool invocation, file edits, streaming, and multi-turn sessions, no need to build your own orchestration layer. Support spans Node.js, Python, Go, .NET, Rust, and Java. This is less a Copilot story and more a platform story; GitHub is positioning itself as the runtime for AI-native dev tooling.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p><strong>&#129504; <a href="https://addyosmani.com/blog/orchestration-tax">The Orchestration Tax</a></strong>: Addy Osmani names something most of us are quietly struggling with: spinning up more agents doesn&#8217;t mean you&#8217;re doing more. Your cognitive bandwidth doesn&#8217;t parallelize. All the judgment to actually steer agents and merge the code they produce still has to route through exactly one serial processor, which is you. Feeling busy is not the same as being productive.</p></li><li><p><strong>&#128216; <a href="https://github.com/AllThingsSmitty/typescript-tips-everyone-should-know">TypeScript Tips Everyone Should Know</a></strong>: This is a compact, no-nonsense reference of TypeScript patterns worth bookmarking. Solid for both onboarding newer devs and as a quick refresh, the kind of thing that pays dividends when you actually internalize it rather than skim it once and forget.</p></li><li><p><strong>&#127757; <a href="/__u/drfeifei.substack.com/p/a-functional-taxonomy-of-world-models">A Functional Taxonomy of World Models</a></strong>: Fei-Fei Li and the World Labs team cut through the noise around &#8220;world models&#8221; and actually define what the term means across different fields. Computer vision, robotics, reinforcement learning, and generative AI all claim to be building world models, and each means something quite different: renderers, simulators, planners. If you care about where AI is actually heading beyond LLMs, this is essential reading.</p></li><li><p><strong>&#9203; <a href="https://jjenzz.com/best-loading-states-are-no-loading-states">The Best Loading States Are No Loading States</a></strong>: Applications end up with skeletons, spinners, shimmer effects, suspense fallbacks, UI whose only job is to occupy the space where data should eventually appear. We&#8217;re all spending a surprising amount of time solving the same problem, and none of it is really product work. This is a sharp essay that argues we&#8217;ve been thinking about this backwards, and the web already had the answer before SPAs came along.</p></li><li><p><strong>&#127916; <a href="https://www.joshwcomeau.com/animation/css-vs-javascript/">CSS vs. JavaScript Animations</a>:</strong> Josh Comeau compares the same animations built across several different strategies and examines the performance implications firsthand, and there&#8217;s some interesting nuance in the results. Not the take you might expect. This is a required reading before you reach for a JS animation library by default.</p></li><li><p><strong>&#128257; <a href="https://www.anthropic.com/institute/recursive-self-improvement">When AI Builds Itself</a></strong>: The Anthropic Institute published something worth sitting with: a look at their actual progress toward recursive self-improvement, with internal data included. Anthropic engineers today ship 8x as much code per quarter as they did between 2021 and 2025, and a growing share of the AI development cycle is now being delegated to AI systems themselves. They&#8217;re careful to say recursive self-improvement isn&#8217;t inevitable, but the framing is unusually candid.</p></li></ul><h1 style="text-align: center;"><strong>The Developer Toolbox</strong></h1><p><strong>&#9997;&#65039; <a href="https://tiptap.dev/docs/hocuspocus/getting-started/overview">Hocuspocus</a></strong></p><p>If you need to add real-time collaborative editing to an app, Hocuspocus from the Tiptap team is the cleanest path there. It&#8217;s a plug-and-play collaboration backend based on Y.js, handling conflict resolution via CRDT so you don&#8217;t have to think about merge logic. Works offline, syncs on reconnect, and pairs naturally with Tiptap, but it&#8217;s flexible enough for other editors too.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item><item><title><![CDATA[WebDevPro #142 Thinking in Transitions: The mental shift React 19 makes hard to ignore ]]></title><description><![CDATA[Grow your Mac app with Setapp.]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-142-thinking-in-transitions</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-142-thinking-in-transitions</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 01 Jun 2026 14:03:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rJQG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong><a href="https://www.vpdae.com/redirect/7kj4k0cgrz8inirkn90zgt5dur3">Grow your Mac app with Setapp. Get around 30K unique impressions in the first days after your app&#8217;s release</a></strong></h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rJQG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 424w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 848w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rJQG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png" width="246" height="369" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:400,&quot;resizeWidth&quot;:246,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 424w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 848w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rJQG!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6d693bf4-0953-48f8-b71b-61b850cda8d7_400x600.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Setapp makes sure your app isn&#8217;t just listed, but seen. Plus, we handle the stuff you don&#8217;t like: distribution, licensing, billing, taxes, and customer support. You build great software; we bring you revenue and valuable feedback to help your app grow. Hope is not a growth strategy. Join Setapp. <strong><a href="https://www.vpdae.com/redirect/7kj4k0cgrz8inirkn90zgt5dur3">Share your app</a>!</strong></p></div><div><hr></div><div class="callout-block" data-callout="true"><h1><strong>Meet the author</strong></h1><p><strong>This piece is written by Rodrigo Lobenwein</strong>. </p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!40oK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 424w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 848w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 1272w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!40oK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png" width="212" height="212" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:398,&quot;width&quot;:398,&quot;resizeWidth&quot;:212,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 424w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 848w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 1272w, /__u/substackcdn.com/image/fetch/$s_!40oK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8ddd27ae-71a6-4636-b1e2-4c769139d8ab_398x398.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>With a background in full stack development across .NET and React, Rodrigo leads a team of senior developers and QA specialists, focusing on the architectural decisions that sit between engineering and product.</p><p>His work centers on helping teams develop the judgment to make the right technical calls not just follow the right frameworks.</p></div><p>React developers learn a reliable reflex early on: update state, let the component re-render, trust the output. That model still holds. The friction starts when the work behind a state update grows large enough for users to actually feel it: a search input that lags on every keystroke while thousands of rows refilter behind it, a tab change that completes instantly in the code but arrives just late enough to make the UI feel sluggish.</p><p>The instinct at that point is to reach for <strong>useMemo</strong> or <strong>useCallback</strong>. Those tools still matter, but they address unnecessary work. Sometimes the problem is different: React is being asked to treat urgent and non-urgent work as if they carry equal weight. Since version 18, the framework has had a better vocabulary for that distinction, and React 19 extends it further. Developing an intuition for when to apply it is what this piece is about.</p><p>Before we dig deeper into this, here&#8217;s a TL;DR you need:</p><ul><li><p><strong>&#129302; <a href="https://blog.google/innovation-and-ai/technology/ai/google-io-2026-all-our-announcements/">Google I/O 2026 pushes deeper into the agentic AI era</a></strong></p></li><li><p><strong>&#128218; <a href="https://storybook.js.org/blog/storybook-10-4/">Storybook 10.4 brings improvements for component-driven development</a></strong></p></li><li><p><strong>&#129513; <a href="https://aurorascharff.no/posts/component-architecture-for-react-server-components/">Designing component architecture for React Server Components</a></strong></p></li><li><p><strong>&#128640; <a href="https://javascript.plainenglish.io/migrating-express-to-next-js-using-ai-agents-antigravity-f48b4c206a8e">Migrating from Express to Next.js with AI agents</a></strong></p></li><li><p><strong>&#128506;&#65039; <a href="https://cassidoo.co/post/clustering-tiles/">What clustering map tiles can teach us about problem solving</a></strong></p></li></ul><h2><strong>Not Every Update Has the Same Urgency</strong></h2><p>Consider a search screen. A user types a character into an input. Two things happen simultaneously: the text field shows the new value, and the results list re-renders against the new query.</p><p>Those updates are related but not equally pressing. The input sits directly under the user&#8217;s fingers &#8212; any delay there registers immediately as a broken experience. The results list matters too, but a brief lag is far less perceptible than a laggy cursor. Most users won&#8217;t notice 100ms of stale results. They will notice 100ms of input latency.</p><blockquote><p><em>Instead of thinking only in terms of &#8220;state changed, render now,&#8221; concurrent React asks you to think about which update is urgent and which can wait for a better moment.</em></p></blockquote><p>That distinction is the conceptual foundation of concurrent rendering.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!gqVy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 424w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 848w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!gqVy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png" width="540" height="185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:185,&quot;width&quot;:540,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 424w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 848w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 1272w, /__u/substackcdn.com/image/fetch/$s_!gqVy!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe59ecd24-ae4d-4f96-81d5-9f21318eff94_540x185.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p style="text-align: center;"><em>Figure 1 - The two tiers of update urgency in concurrent React</em></p><h2><strong>What Automatic Batching Actually Solved</strong></h2><p>Before transitions make sense, it helps to separate them from automatic batching, which is an adjacent improvement that solves a different problem.</p><p>In React 17, batching was largely confined to synchronous React event handlers. Multiple state updates inside a Promise callback, a <strong>setTimeout</strong>, or a <strong>fetch</strong> response were often processed as separate renders. More renders than necessary, without much upside.</p><p>The <strong>createRoot</strong> API in version 18 extended automatic batching across more update sources: timers, Promise handlers, and most async callbacks. Many apps shed redundant render cycles without changing a single component.</p><p>Batching is a reduction in render quantity. Transitions are about render priority. Even with batching in place, a single batched update that includes expensive list rendering can make an input feel sticky. Batching has no way to distinguish which part of that update the user is waiting on. Transitions provide that signal.</p><h2><strong>Marking Lower-Priority Work with startTransition</strong></h2><p>The <strong>startTransition</strong> API lets you label a state update as non-urgent. &#8220;Non-urgent&#8221; does not mean &#8220;unimportant&#8221; &#8212; it means React does not need to hold up higher-priority feedback to process it first.</p><p>In a search interface, the pattern looks like this:</p><p><code>const handleOnChange = (event) =&gt; {</code></p><p><code>setInputValue(event.target.value); // urgent: runs first</code></p><p><code>startTransition(() =&gt; {</code></p><p><code>setQuery(event.target.value); // deferred: can wait</code></p><p><code>});</code></p><p><code>};</code></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!O3sN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 424w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 848w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!O3sN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png" width="617" height="243" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:243,&quot;width&quot;:617,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 424w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 848w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 1272w, /__u/substackcdn.com/image/fetch/$s_!O3sN!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcb6a6ba7-cefb-414c-ba8e-29d62dc531ed_617x243.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em>Figure 2 &#8212; Without transitions, an expensive list render blocks the input. With transitions, the input stays instant and list work is interruptible.</em></p><p>The transition wraps the update that causes the expensive rendering, not the expensive code itself. React is not being told to skip the work; it&#8217;s being given the context to sequence it correctly.</p><p>A reliable heuristic: reach for a transition when a state update can trigger a large render, and the user does not need to see the result of that update instantaneously. Filtering a long list is a candidate. Updating the visible value in an input is not.</p><p>When the UI needs to acknowledge that transition work is still in progress, <strong>useTransition</strong> returns both pieces:</p><p><code>const [isPending, startTransition] = useTransition();</code></p><p>Use <strong>isPending</strong> for lightweight signals: dimming stale content, showing a small spinner. Resist the urge to let it take over the layout, its value is in supporting the interaction, not replacing it.</p><h2><strong>When useDeferredValue Fits Better</strong></h2><p>Both <strong>startTransition</strong> and <strong>useDeferredValue</strong> address the same class of problem. The choice between them turns on where you have control in the component tree.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ZGgK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ZGgK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png" width="586" height="252" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:252,&quot;width&quot;:586,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 424w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 848w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ZGgK!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F52e55659-fd19-4886-a537-8a0c23a3384f_586x252.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><em>Figure 3 &#8212; The decision comes down to code ownership. Both APIs produce equivalent scheduling outcomes</em></p><p>When the component owns both the input and the expensive update, <strong>startTransition</strong> is usually the cleaner solution:</p><p><code>function SearchPage() {</code></p><p><code>const [inputValue, setInputValue] = useState(&#8221;&#8220;);</code></p><p><code>const [query, setQuery] = useState(&#8221;&#8220;);</code></p><p><code>const [isPending, startTransition] = useTransition();</code></p><p><code>const handleOnChange = (event) =&gt; {</code></p><p><code>const nextValue = event.target.value;</code></p><p><code>setInputValue(nextValue);</code></p><p><code>startTransition(() =&gt; { setQuery(nextValue); });</code></p><p><code>};</code></p><p><code>return (</code></p><p><code>&lt;&gt;</code></p><p><code>&lt;input value={inputValue} onChange={handleOnChange} /&gt;</code></p><p><code>&lt;Results query={query} dimmed={isPending} /&gt;</code></p><p><code>&lt;/&gt;</code></p><p><code>);</code></p><p><code>}</code></p><p>The component has direct access to both setState calls, so it can be explicit about which one should yield.</p><p>When the value arrives from outside, from a parent, a form library, route state, or a shared component, the results component can&#8217;t reach the original setState. It can, however, choose to render against a deferred version of the value:</p><p><code>function ResultsPanel({ query }) {</code></p><p><code>const deferredQuery = useDeferredValue(query);</code></p><p><code>const isStale = query !== deferredQuery;</code></p><p><code>const items = useMemo(</code></p><p><code>() =&gt; filterLargeList(deferredQuery),</code></p><p><code>[deferredQuery]</code></p><p><code>);</code></p><p><code>return &lt;Results items={items} dimmed={isStale} /&gt;;</code></p><p><code>}</code></p><p>Here, <strong>query</strong> updates immediately in the parent; <strong>deferredQuery</strong> trails behind when rendering is busy. The expensive filtering follows the deferred value, so the input stays responsive regardless of how long the results take.</p><h2><strong>How This Changes Component Design</strong></h2><p>Concurrent rendering does not mean every piece of state needs a priority label. Most updates are cheap, and most components need no transitions whatsoever.</p><p>The practical design question is more targeted: what part of this interaction must feel instant, and what part can follow slightly behind? Asking it tends to clarify both component boundaries and state placement. State driving the element the user is actively touching belongs on the urgent path. State driving a large subtree, expensive filtering, or a complex visual transformation is often a better fit for transition work.</p><p>One underrated benefit of this model is interruptibility. Transition rendering can be abandoned. If a user types another character before the previous results render finishes, React discards the stale render and restarts with the latest input. That&#8217;s a meaningful improvement over older <strong>setTimeout</strong>-based workarounds, where React had no clear signal about which work was still relevant.</p><h2><strong>The Shift Worth Internalizing</strong></h2><p>The APIs themselves are small. <strong>startTransition</strong> wraps a state update. <strong>useDeferredValue</strong> returns a lagging version of a value. The more durable change is in how you reason about responsiveness.</p><p>Older React code tends to treat rendering as a single block: state changes, render fires, UI reflects the result. Concurrent React asks for a slightly different mental model &#8212; one closer to how interface designers think about interaction. The thing under the user&#8217;s control responds first. Work that improves the screen but doesn&#8217;t need to be immediate follows behind.</p><blockquote><p><em>React performance is not only about preventing work. It is also about sequencing work so the app feels responsive where it matters most.</em></p></blockquote><h2><strong>Key Takeaways</strong></h2><ul><li><p>React 18 expanded automatic batching via <strong>createRoot</strong>, cutting unnecessary renders across async update sources.</p></li><li><p><strong>startTransition</strong> schedules a state update as lower priority without preventing it from running.</p></li><li><p><strong>useTransition</strong> returns <strong>[isPending, startTransition]</strong>, letting you give subtle in-progress feedback while expensive work continues.</p></li><li><p><strong>useDeferredValue</strong> fits when a component receives a value from outside and can tolerate rendering against a slightly older version.</p></li><li><p>Transition rendering is interruptible &#8212; stale work is abandoned when newer, more urgent updates arrive.</p></li><li><p>The concurrent model reframes performance: not only as avoiding unnecessary work, but as sequencing necessary work by urgency.</p></li></ul><p><em>Want to read more on the topic? <a href="https://www.amazon.com/React-Native-cross-platform-JavaScript-TypeScript/dp/1837020299/ref=sr_1_1_sspa">React and React Native, Sixth Edition</a> covers React 19 and React Native from the ground up.</em></p><div><hr></div><div class="callout-block" data-callout="true"><p><strong>&#127873; GIVEAWAY &#8212; JUNE 2026</strong></p><h2><strong>Build AI Products Faster with Cursor, Lovable &amp; Windsurf</strong></h2><p>Subscribe to <strong><a href="/__u/packtbuildwithai.substack.com/">BuildWithAI</a></strong> and get our complete <em><strong>Vibe Coding with Cursor, Windsurf, and Lovable</strong></em> delivered free to your inbox. </p><p>Learn the exact workflows builders are using to:</p><p>     &#9989; Turn ideas into working products in hours</p><p>     &#9989; Build MVPs without getting stuck in code</p><p>     &#9989; Use Cursor, Lovable &amp; Windsurf effectively together</p><p>     &#9989; Launch faster with AI-assisted development</p></div><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p><strong>&#129302; <a href="https://blog.google/innovation-and-ai/technology/ai/google-io-2026-all-our-announcements/">Google I/O 2026 pushes deeper into the agentic AI era</a></strong>: Google used I/O 2026 to double down on AI across its entire ecosystem, with major updates to Gemini, Search, developer tools, and agent-based workflows. The company introduced new models like Gemini Omni and Gemini 3.5 Flash, expanded its agent platform, and continued reshaping Search around AI-powered experiences. The bigger theme was clear: Google is moving beyond AI as a feature and positioning it as the foundation across products, workflows, and developer tooling.</p></li><li><p><strong>&#128218; <a href="https://storybook.js.org/blog/storybook-10-4/">Storybook 10.4 brings improvements for component-driven development</a>: Storybook 10.4</strong> introduces a range of updates aimed at improving the developer experience around building, testing, and documenting UI components. The release continues Storybook&#8217;s focus on making component-driven development more efficient, with enhancements across workflows, tooling, and framework support. As frontend applications grow in complexity, tools like Storybook are becoming increasingly important for maintaining consistency and speeding up UI development.</p></li><li><p>&#128640; <strong><a href="https://astro.build/blog/astro-640/">Astro 6.4 goes full Rust on Markdown</a></strong>: <strong>Astro 6.4</strong> joins the Rust rewrite club with S&#228;tteri, a new Markdown processor that cut over a minute off real-world build times. It&#8217;s opt-in for now via the new <code>markdown.processor</code> API, but the team has flagged it as the likely future default, migration cost being the remark/rehype plugin compatibility.</p></li><li><p>&#129302; <strong><a href="https://www.anthropic.com/news/claude-opus-4-8">Claude Opus 4.8 drops as an incremental but meaningful upgrade</a></strong>: Better benchmark scores, sharper agentic judgment, and notably improved honesty (4&#215; less likely to let code flaws slip by unremarked). Pricing stays the same. Also shipping alongside it: effort controls on claude.ai, and a &#8220;dynamic workflows&#8221; feature in Claude Code that can spin up hundreds of parallel subagents for codebase-scale tasks.</p></li><li><p>&#127760; <strong><a href="https://web.dev/blog/baseline-digest-apr-2026">Web platform catches up on quality-of-life</a></strong>: <strong>April&#8217;s Baseline digest</strong> brought some solid platform wins: <code>contrast-color()</code> auto-picks readable text against any background, <code>Math.sumPrecise()</code> fixes floating-point drift in array sums, the <code>&lt;search&gt;</code> element now hands you an ARIA landmark for free, and ARIA attribute reflection means cleaner <code>element.ariaExpanded</code> syntax over <code>setAttribute</code>.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p><strong>&#9889; <a href="https://performance.dev/how-is-linear-so-fast-a-technical-breakdown">How is Linear so fast? A technical breakdown</a></strong>: Linear has become the benchmark for fast, responsive web applications, and this deep dive explores the engineering decisions behind that experience. From frontend architecture to rendering strategies and performance optimizations, the article breaks down the techniques that help the product feel almost instantaneous. The broader lesson is that performance is rarely the result of a single breakthrough. It comes from dozens of deliberate decisions across the stack, all working together to reduce friction and keep users in flow.</p></li><li><p><strong>&#129513; <a href="https://aurorascharff.no/posts/component-architecture-for-react-server-components/">Designing component architecture for React Server Components</a></strong>: React Server Components change more than just where code runs. They also influence how applications are structured and how responsibilities are divided between components. This article explores architectural patterns for organizing components in an RSC-based application and avoiding common pitfalls. As more frameworks embrace server-first rendering, understanding these patterns is becoming increasingly important. The challenge is no longer just building components, but deciding which ones belong on the server and which ones truly need to run on the client.</p></li><li><p><strong>&#128196; <a href="https://developer-api.foxit.com/developer-blogs/use-cases-workflow-examples/automated-document-pipelines/foxit-mcp-server-ai-pdf-tools/">Bringing AI workflows to PDF-heavy applications</a></strong>: As AI agents become more capable, one challenge remains consistent: documents. This article explores how Foxit&#8217;s MCP Server connects AI systems with PDF workflows, enabling tasks such as document analysis, extraction, and processing without requiring developers to build custom integrations from scratch. The bigger trend is the rise of infrastructure that helps AI agents interact with existing business systems. Rather than generating text in isolation, agents are increasingly being equipped to work with the documents and workflows that power day-to-day operations.</p></li><li><p><strong>&#128640; <a href="https://javascript.plainenglish.io/migrating-express-to-next-js-using-ai-agents-antigravity-f48b4c206a8e">Migrating from Express to Next.js with AI agents</a></strong><a href="https://javascript.plainenglish.io/migrating-express-to-next-js-using-ai-agents-antigravity-f48b4c206a8e">:</a> This case study explores using AI agents to help migrate an application from Express to Next.js. Rather than focusing solely on code generation, it examines how agents can assist with larger engineering tasks such as understanding existing architecture, planning migrations, and implementing changes across a codebase. The interesting takeaway is that AI is increasingly being used as a collaborator on complex development projects. The challenge is no longer whether AI can write code, but how effectively it can help developers navigate and transform existing systems.</p></li><li><p><strong>&#128506;&#65039; <a href="https://cassidoo.co/post/clustering-tiles/">What clustering map tiles can teach us about problem solving</a></strong>: In this post, Cassidy Williams walks through the challenge of clustering map tiles and the thought process behind solving it. Rather than focusing solely on the final solution, the article highlights the experimentation, trade-offs, and iterative thinking that go into tackling a seemingly simple problem. It&#8217;s a good reminder that software engineering is often less about finding the perfect algorithm and more about breaking complex problems into manageable pieces and refining solutions along the way.</p></li></ul><h1 style="text-align: center;"><strong>The Developer Toolbox</strong></h1><ul><li><p><strong>&#128293; <a href="https://flueframework.com/">Build reactive web apps without a framework runtime</a></strong>: Modern frontend frameworks often trade simplicity for abstractions. <strong>Flue</strong> takes a different approach, offering a reactive UI framework that focuses on minimal overhead, direct DOM updates, and a lightweight development experience. The project aims to deliver fine-grained reactivity without the complexity or runtime costs typically associated with larger frameworks. If you&#8217;re interested in exploring alternative approaches to building fast, reactive web applications, Flue is worth a look.</p></li><li><p><strong>&#9000;&#65039; <a href="https://jamiebuilds.github.io/tinykeys/">Handle keyboard shortcuts with minimal overhead</a></strong>: Keyboard shortcuts can quickly become messy as applications grow. <strong>Tinykeys</strong> is a lightweight JavaScript library that makes it easy to define and manage keyboard shortcuts with a simple, intuitive API. At under 1 KB, it supports complex key combinations and sequences without adding unnecessary weight to your application. If you&#8217;re building power-user features, command palettes, or productivity-focused interfaces, Tinykeys offers a clean way to handle keyboard interactions.</p></li></ul><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item><item><title><![CDATA[The CSS-First Mindset Modern Frontends Need Again]]></title><description><![CDATA[For years, frontend development has carried a quiet assumption: when an interface needs to respond, animate, toggle, validate, or remember state, JavaScript should take the lead.]]></description><link>https://packtwebdevpro.substack.com/p/the-css-first-mindset-modern-frontends</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/the-css-first-mindset-modern-frontends</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 25 May 2026 14:03:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!KWnM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For years, frontend development has carried a quiet assumption: when an interface needs to respond, animate, toggle, validate, or remember state, JavaScript should take the lead. That instinct made sense for a long time. Browsers were inconsistent, CSS was limited, and many interaction patterns genuinely needed script-heavy solutions to feel polished.</p><p>That landscape has changed. Modern HTML and CSS now handle far more than layout and decoration. They can respond to form state, respect system preferences, manage disclosure patterns, animate page transitions, create motion paths, style modal backdrops, and support accessible behavior that developers previously had to rebuild from scratch. The question is no longer &#8220;Can JavaScript do this?&#8221; because, of course, it can. The better question is &#8220;Should JavaScript be responsible for this?&#8221;</p><p>A CSS-first mindset does not mean rejecting JavaScript. It means giving the browser a fair chance before adding another dependency, state handler, event listener, or UI library. It asks developers to start with semantic HTML, layer CSS for presentation and interaction, and reserve JavaScript for the work it is uniquely good at: data fetching, complex application logic, persistence, coordination across systems, and genuinely dynamic behavior.</p><p>That small shift can change the quality of a frontend codebase. Interfaces become lighter, easier to reason about, more resilient when something fails, and often more accessible by default. For intermediate developers, this is one of the most useful habits to build: not writing less code for the sake of minimalism, but choosing the browser-native path when it gives users a better result.</p><p>Before we dig deeper into this, here&#8217;s a TL;DR you need:</p><ul><li><p>&#129408; <em><strong><a href="https://github.com/oven-sh/bun/pull/30412">Bun&#8217;s massive Rust rewrite reignites the JavaScript tooling conversation</a></strong></em></p></li><li><p>&#128274; <em><strong><a href="https://www.youtube.com/watch?v=gwTQLZSIlsU">TanStack&#8217;s supply chain compromise shows how fragile npm security still is</a></strong></em></p></li><li><p>&#127760; <em><strong><a href="https://developer.chrome.com/blog/install-element-ot">Chrome and Edge explore a native &lt;install&gt; element for PWAs</a></strong></em></p></li><li><p>&#128274; <em><strong><a href="https://socket.dev/blog/antv-packages-compromised">Shai Hulud returns with another package supply chain hit</a></strong></em></p></li><li><p>&#127909; <em><strong><a href="https://www.youtube.com/watch?v=6moPS3AAbe4">TanStack Start enters the framework race with a different philosophy</a></strong></em></p></li><li><p>&#129718; <em><strong><a href="https://github.com/addyosmani/critical">Critical 8.0 brings CSS performance back to first paint</a></strong></em></p></li></ul><h1><strong>The cost of reaching for JavaScript too early</strong></h1><p>JavaScript is powerful, but power has a cost. Every script has to be downloaded, parsed, compiled, and executed. Every custom interaction adds more behavior to maintain. Every dependency introduces another layer of updates, bundle impact, and possible failure. On a fast machine and a strong connection, those costs may feel invisible. On a slower device, a busy network, or a page already carrying analytics, ads, and third-party widgets, they become much easier to feel.</p><p>The issue is not that JavaScript is bad. The issue is that many teams use it as a default even when the browser already has the behavior built in. A simple accordion becomes a component with state. A modal becomes a focus-management exercise. A navigation highlight becomes a DOM query. A theme switcher becomes a script-first feature before CSS has been allowed to handle the visual logic.</p><div class="callout-block" data-callout="true"><p>This matters because users experience the final result, not the elegance of our tooling. A lighter interface often loads faster, responds sooner, and fails more gracefully. It also tends to be easier for the next developer to understand. When structure lives in HTML, behavior comes from native elements, and styling expresses state through CSS, the codebase has fewer hidden moving parts.</p></div><h1><strong>CSS is becoming contextual, not just decorative</strong></h1><p>One of the most important changes in modern CSS is that it can respond to context. The :has() selector is a good example. For a long time, CSS could style children based on parents, descendants based on ancestors, and siblings in limited directions, but it struggled with parent-level conditions. Developers often added JavaScript just to say, &#8220;Style this container differently when something inside it is checked, active, hovered, or present.&#8221;</p><p>That kind of logic now fits naturally into CSS. A fieldset can react when a checkbox inside it is selected. A navigation item can show an indicator only when it contains a submenu. A card list can blur non-hovered cards when one card is active. A layout can switch when a form control changes state. These are visual responses to visual conditions, which makes CSS a better home for them than JavaScript.</p><p>The real takeaway is not simply that :has() is useful. It is that CSS is moving closer to how designers and developers think about interfaces. Components rarely exist in isolation. Their styling depends on state, nearby elements, available space, user preferences, and interaction context. The more CSS can express those relationships directly, the less we need to create artificial classes or scripts just to bridge the gap.</p><div class="callout-block" data-callout="true"><p>There is still a performance and maintainability angle to consider. Broad relational selectors can ask the browser to do more work than necessary, especially on large pages. A focused selector that checks for a nearby .active element is easier to maintain than one that searches through a deeply nested structure. Like any powerful feature, :has() rewards restraint.</p></div><h1><strong>Native elements carry behavior we often forget to value</strong></h1><p>A CSS-first approach becomes even stronger when paired with semantic HTML. Elements such as &lt;dialog&gt;, &lt;details&gt;, and &lt;summary&gt; are not just markup conveniences. They carry interaction behavior that many teams have spent years recreating.</p><p>Take modals. A custom modal usually needs background blocking, focus handling, Escape-key behavior, ARIA wiring, open and close logic, and backdrop styling. The native &lt;dialog&gt; element handles much of this through the browser. With showModal(), the page gets modal behavior without a large custom system. CSS can then style the dialog and its ::backdrop, while a small amount of JavaScript handles the moment it opens.</p><p>Accordions tell a similar story. The &lt;details&gt; and &lt;summary&gt; elements provide disclosure behavior without building state from scratch. They work with keyboard interaction, keep content structured, and remain understandable even when CSS enhancements are unavailable. When multiple details elements share a name, they can behave like a grouped accordion where opening one closes another. That is a meaningful reduction in custom logic.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!KWnM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 424w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 848w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!KWnM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png" width="1448" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 424w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 848w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 1272w, /__u/substackcdn.com/image/fetch/$s_!KWnM!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F127ad044-e3ad-4bfe-9f00-1abd397cd2f4_1448x1086.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>This is where developers can level up quickly. Instead of asking, &#8220;Which package should I install for this UI pattern?&#8221; ask, &#8220;Does HTML already have a primitive for this?&#8221; Native primitives may not cover every product requirement, but they often provide a stronger baseline than a custom implementation written under deadline pressure.</p></div><h1><strong>Accessibility improves when the browser keeps its job</strong></h1><p>Many accessibility problems come from replacing native behavior with custom behavior and then forgetting to rebuild all the pieces users rely on. A div that behaves like a button still needs keyboard support, focus styles, roles, states, and expected interaction patterns. A custom accordion still needs to communicate its expanded state. A custom modal still needs to manage focus without trapping users in awkward ways.</p><p>CSS-first and HTML-first thinking reduces that risk. Native controls already know a lot about input methods, assistive technologies, system preferences, and user expectations. A range input can be dragged, focused, and adjusted with the keyboard. A summary element can be opened without a custom click handler. A dialog can announce itself with the right labeling when structured properly.</p><p>This does not remove responsibility from developers. Accessibility still requires care. Motion should respect prefers-reduced-motion. Theme choices should preserve contrast. Generated content should not be the only place important information exists, because pseudo-elements are not always announced. A CSS-generated counter, for example, may need an accessible label or equivalent text in the markup.</p><div class="callout-block" data-callout="true"><p>The practical lesson is simple: let native behavior do as much as it reasonably can, then enhance it thoughtfully. Accessibility is not a polish step at the end. It is often a direct result of choosing the right primitive at the beginning.</p></div><h1><strong>Progressive enhancement is back in a practical way</strong></h1><p>Modern CSS features arrive at different speeds across browsers. That can make teams cautious, especially when they remember older eras of painful browser inconsistencies. The healthier approach is not to avoid new CSS entirely. It is to use progressive enhancement deliberately.</p><p>Some features are safe because unsupported browsers simply fall back to a usable experience. Smooth scrolling can fall back to normal anchor jumps. Accordion animations can fall back to instant open and close behavior. A masked comparison slider can still expose a native range input with a simpler presentation. View transitions can be ignored by unsupported browsers while navigation continues to work.</p><p>Feature queries such as @supports help developers layer enhancements only where browsers understand them. User preference queries such as prefers-color-scheme and prefers-reduced-motion let sites adapt to the person using them rather than forcing one visual experience on everyone. This is not just technical neatness. It is a more respectful way to build for a web that runs across different devices, settings, and constraints.</p><div class="callout-block" data-callout="true"><p>The best progressive enhancement does not make the baseline feel broken. It starts with functional HTML, improves the experience with widely supported CSS, and adds newer capabilities where they make the interface smoother or more expressive. The result feels modern without becoming fragile.</p></div><h1><strong>Motion needs purpose, not just possibility</strong></h1><p>CSS can now create polished motion without JavaScript timers or heavy libraries. Infinite logo sliders, animated borders, view transitions, motion paths, and scroll-driven effects can all be handled mostly or entirely through CSS. That is exciting, but motion should earn its place.</p><p>A partner logo strip that moves continuously may add energy, but it should pause on hover or focus so users can inspect it. Page transitions can make navigation feel smoother, but they should not slow users down or become disorienting. A glowing animated border may draw attention to a feature card, but the interface should still feel calm when motion is reduced.</p><p>This is where CSS gives developers a useful advantage. Animations can be expressed declaratively, paused with state selectors, and disabled through media queries. Rather than manually coordinating timers, listeners, and cleanup, the browser can manage the animation lifecycle. That usually leads to smoother performance and simpler code.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!GKae!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 424w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 848w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!GKae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png" width="1448" height="1086" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1086,&quot;width&quot;:1448,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 424w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 848w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 1272w, /__u/substackcdn.com/image/fetch/$s_!GKae!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88fd5b46-5e85-4664-9b7e-a8e1a0b50b23_1448x1086.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>The deeper takeaway is that CSS motion works best as communication. It can show continuity, guide attention, reveal relationships, or make a state change feel less abrupt. Motion that exists only because it is possible tends to age badly. Motion that helps users understand what changed is much more likely to belong.</p></div><h1><strong>The future frontend stack may be smaller than we expected</strong></h1><p>The frontend ecosystem often moves by adding layers. More state management, more rendering strategies, more build tools, more component abstractions. Many of those tools solve real problems, especially in application-heavy environments. But the browser itself has also been moving forward, and sometimes our habits have not caught up.</p><p>A smaller stack does not mean a less capable interface. It can mean choosing CSS variables for theming before inventing a theme engine. It can mean using prefers-color-scheme before writing theme detection logic. It can mean using &lt;dialog&gt; before importing a modal package. It can mean using scroll-margin-top before writing scroll offset JavaScript. It can mean allowing CSS counters to number content instead of maintaining numbers by hand.</p><p>The strongest teams will not be the ones that avoid JavaScript. They will be the ones that know when not to spend it. JavaScript should still handle persistence for a saved theme choice, fetch fresh data, coordinate complex interactions, and power application logic. But it should not automatically inherit every small UI responsibility simply because it can.</p><div class="callout-block" data-callout="true"><p>A CSS-first mindset gives developers more options. It helps teams ship less code, lean on browser behavior, and build interfaces that survive better across conditions. Most importantly, it brings the craft of frontend development closer to the web&#8217;s original strength: resilient layers that work together rather than one layer trying to do everything.</p></div><h1><strong>Takeaways</strong></h1><p>Modern CSS is no longer just a styling layer. It can express state, context, user preferences, motion, and responsive interaction patterns that once required JavaScript. Intermediate developers should treat CSS and semantic HTML as active parts of interface architecture, not just the final presentation pass.</p><p>The best use of JavaScript is intentional. Reach for it when the interface needs data, persistence, complex logic, or behavior the browser cannot provide on its own. For many everyday UI patterns, native HTML and CSS now provide a lighter, more accessible, and more maintainable starting point.</p><p>Progressive enhancement is the safest way to adopt newer features. Build a solid baseline first, then layer modern selectors, animations, masks, transitions, and preference-aware behavior where support allows. Users get a functional experience everywhere, and a richer one in capable browsers.</p><p>The real mindset shift is simple: before adding script, pause and ask whether the browser already understands the job. More often than many developers expect, the answer is yes.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!IGjc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!IGjc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 424w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 848w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 1272w, /__u/substackcdn.com/image/fetch/$s_!IGjc!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0016f1b2-7b2b-42ee-b1b2-4abffea02dc0_1536x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p>&#129302; <strong><a href="https://github.com/google/ax">Google open-sources Ax for distributed AI agents</a></strong>: Google has open-sourced <strong>Ax</strong>, a distributed runtime designed for building and coordinating AI agents across systems. The project focuses on managing agent execution at scale, reflecting growing interest in infrastructure for multi-agent workflows rather than standalone AI interactions.It&#8217;s another sign that the AI ecosystem is shifting from individual prompts toward orchestrated agent-based systems.</p></li><li><p>&#129512;<strong> <a href="https://github.com/oven-sh/bun/pull/30412">Bun&#8217;s Rust rewrite finally lands</a></strong>: Bun&#8217;s long-discussed Rust rewrite has now been merged, which gives the runtime another big moment in the JavaScript tooling conversation. The interesting part is not only the rewrite itself, but <a href="https://news.ycombinator.com/item?id=48019226">the debate around how it was produced</a>. With AI-ported code under scrutiny, this becomes a useful reminder that speed still needs engineering taste, review discipline, and trust in the codebase. For developers, Bun remains one of the most exciting projects to watch, but this merge also shows <a href="https://news.ycombinator.com/item?id=48150900">how closely the community is now examining</a> the way modern tooling gets built.</p></li><li><p>&#127760; <strong><a href="https://developer.chrome.com/blog/install-element-ot">Browsers may soon get a trusted PWA install button</a></strong>: Chrome and Edge are exploring a new &lt;install&gt; HTML element that would let browsers render a trusted install button for PWAs. That might sound like a small interface detail, but it points to a bigger shift in how seriously browsers want to treat installable web apps. PWA adoption has always had a discoverability problem. A native, browser-controlled install element could make the experience feel clearer, safer, and more consistent for users, while giving developers a cleaner path to promote app installation without relying on custom prompts.</p></li><li><p>&#127909; <strong><a href="https://www.youtube.com/watch?v=gwTQLZSIlsU">Fireship explains how one PR hijacked the npm registry</a></strong>: The npm ecosystem had another uncomfortable security lesson, and Fireship&#8217;s breakdown captures why this matters for everyday developers. A single compromised or malicious pull request can ripple through the dependency graph faster than most teams can react. JavaScript&#8217;s strength has always been its massive open ecosystem, but that same scale makes supply chain security everyone&#8217;s problem.</p></li><li><p>&#128274; <strong><a href="https://socket.dev/blog/antv-packages-compromised">Shai Hulud returns with another package supply chain hit</a></strong>: Shai Hulud struck again this week, affecting hundreds of packages and adding another serious entry to the JavaScript supply chain security story. At the same time, <a href="https://socket.dev/blog/node-ipc-package-compromised">node-ipc was also infected with a credentials stealer</a>, which makes this feel less like an isolated scare and more like a pattern developers need to plan around. The practical question for teams is no longer whether package compromise can happen. It is how quickly they can detect, contain, rotate credentials, and recover when it does.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p><strong>&#128250; <a href="https://www.youtube.com/watch?v=6moPS3AAbe4">TanStack Start takes its shot at the framework conversation</a></strong>: Tanner Linsley&#8217;s conversation on TanStack Start is worth watching because it is a candid look at what it takes to compete in a space where Next.js already shapes many developers&#8217; expectations. TanStack has earned trust through focused, composable tools, and Start is trying to bring that same philosophy into full-stack app development. The bigger story is how much appetite still exists for frameworks that give developers more control without asking them to give up modern conventions.</p></li><li><p>&#129513; <strong><a href="https://denodell.com/blog/browsers-treat-big-sites-differently">Browser engines quietly patch the web for popular sites</a></strong>: Some browsers ship built-in <a href="https://searchfox.org/firefox-main/source/browser/extensions/webcompat/data/interventions">tweaks for specific popular sites</a> when those sites do not render correctly by default. Firefox and Safari both do this, which says a lot about the messy reality behind the &#8220;web standards&#8221; ideal. Developers often talk about browser compatibility as though it is only about specs, but the web also runs on pragmatism. When a major site breaks, browser teams sometimes bend a little to preserve the user experience. It is a fascinating glimpse into the invisible maintenance work that keeps the modern web usable.</p></li><li><p>&#129504; <strong><a href="https://justsitandgrin.im/posts/native-all-the-way-until-you-need-text/">The limits of native code deserve a closer look</a></strong>: Artem Loenko&#8217;s piece on native code lands at a useful time, especially as more web developers think about performance through the lens of Rust, WebAssembly, and lower-level tooling. Native code can be powerful, but it is not a magic escape hatch. Complexity, portability, safety, developer experience, and maintenance still matter. The best engineering choices usually come from knowing where native code helps, where it complicates things, and where higher-level tools already do the job well enough.</p></li><li><p>&#127793; <strong><a href="https://nesbitt.io/2026/05/19/dumb-ways-for-an-open-source-project-to-die.html">Open source projects can die in surprisingly ordinary ways:</a></strong><a href="https://nesbitt.io/2026/05/19/dumb-ways-for-an-open-source-project-to-die.html">:</a> This piece is a sharp reminder that open source projects do not always fail because of dramatic technical problems. Sometimes they fade because maintainers burn out, governance is unclear, onboarding is painful, or the project quietly stops feeling worth the effort. For developers who depend on open source every day, it is worth reading with empathy. Healthy projects need more than stars, downloads, and GitHub activity. They need people, process, trust, and a path for new contributors to become part of the work.</p></li><li><p>&#129516; <strong><a href="https://joyeecheung.github.io/blog/2026/05/18/how-nodejs-and-v8-keep-each-other-working/">How Node.js and V8 keep each other working</a></strong>: Joyee Cheung&#8217;s deep dive into Node.js and V8 is a great read for anyone who wants to understand the machinery behind the runtime we use so casually. Node&#8217;s relationship with V8 is not a simple dependency story. It is an ongoing coordination effort across APIs, internals, performance work, compatibility, and release timing. This is the kind of behind-the-scenes engineering that rarely gets the spotlight, but it directly shapes what JavaScript developers can build and how reliably their apps run.</p></li></ul><h1 style="text-align: center;"><strong>Practical AI for Working Developers</strong></h1><p>AI is moving fast, and for a lot of developers, keeping up still feels like learning through trial and error.</p><p><strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com">BuildWithAI</a></strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com"> </a>is Packt&#8217;s newsletter for engineers who want to move beyond AI headlines and start using it in real projects.</p><p>Backed by Packt&#8217;s 7,000+ tech books, courses, and expert resources across 1,000+ technologies, each issue brings you practical workflows, carefully chosen resources, and implementation guidance you can actually apply. Subscribe <strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com">here</a></strong>.</p><h1 style="text-align: center;"><strong>The Developer Toolbox</strong></h1><ul><li><p>&#9889; <strong><a href="https://github.com/addyosmani/critical">Critical 8.0</a></strong>: Addy Osmani&#8217;s Critical 8.0 helps extract and inline above-the-fold CSS into HTML, which makes it useful for anyone still paying attention to real-world page speed. Performance work often gets buried under bigger framework conversations, but rendering fast still matters. Critical is a reminder that some of the highest-impact optimizations are practical, focused, and close to the browser&#8217;s first paint. For teams working on content-heavy sites, landing pages, or performance-sensitive web apps, this is a tool worth revisiting.</p></li><li><p>&#128467;&#65039; <strong><a href="https://svar.dev/blog/svar-calendar-for-react-svelte-vue/">SVAR Calendar brings a flexible calendar component across frameworks</a></strong>: SVAR Calendar offers a calendar component for React, Svelte, and Vue, with an MIT-licensed core and a commercial version for extended use cases. Calendar UI is one of those deceptively hard pieces of product development. It looks familiar until you need recurring events, custom views, localization, styling, and framework compatibility. SVAR&#8217;s cross-framework approach makes it interesting for teams that want a polished calendar foundation without building every interaction from scratch. The <a href="https://svar.dev/demos/calendar/">live demo</a> is worth a quick look.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[WebDevPro #140: The Hidden Complexity Behind "Simple" Three.js Scenes ]]></title><description><![CDATA[Three.js projects often begin with a deceptively simple moment.]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-140-the-hidden-complexity</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-140-the-hidden-complexity</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 18 May 2026 14:03:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-Epb!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c609e43-4006-496a-9adf-84620ff6b584_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Three.js projects often begin with a deceptively simple moment.</p><p>A cube appears on screen. A light illuminates the scene. The camera moves smoothly. An animation loop runs continuously.</p><p>At that stage, the experience feels manageable. The structure seems straightforward: create a scene, add geometry, configure lighting, and render continuously.</p><p>The early feedback loop is rewarding because visible progress happens quickly. A functioning 3D scene can come together in relatively little code, which creates the impression that the difficult part has already been solved.</p><p>But complexity in Three.js rarely appears immediately.</p><p>It emerges gradually through interaction between systems: lighting affects material perception, camera positioning changes spatial readability, shadows introduce rendering overhead, responsiveness alters composition, animation timing affects scene coherence, and asset complexity impacts performance.</p><p>The difficult part is not rendering the first object.</p><p>The difficult part is maintaining coherence as the scene evolves.</p><p>This is why many impressive Three.js demos struggle to become stable, scalable product experiences. The challenge is rarely the first rendered frame. The challenge is managing the growing interaction between rendering, animation, lighting, responsiveness, and scene organization over time.</p><p>Before we get into it, here&#8217;s a sneak peek of this week&#8217;s highlights:</p><ul><li><p><strong>&#128737;&#65039;</strong><em><strong><a href="https://vercel.com/changelog/next-js-may-2026-security-release">Next.js patches 13 vulnerabilities that teams should not leave to the firewall</a></strong></em></p></li><li><p><strong>&#127959;&#65039; </strong><em><strong><a href="https://x.ai/news/anthropic-compute-partnership">Anthropic&#8217;s Colossus deal puts AI infrastructure back in the spotlight</a></strong></em></p></li><li><p><strong>&#129302; </strong><em><strong><a href="https://agentfield.ai/github">AgentField turns multiple coding agents into one TypeScript workflow</a></strong></em></p></li><li><p><strong>&#129516; </strong><em><strong><a href="https://sylvie.fyi/posts/react2shell/">React2Shell shows how curiosity can become an ecosystem-level security story</a></strong></em></p></li><li><p><strong>&#9883;&#65039; </strong><em><strong><a href="https://www.youtube.com/watch?v=5QM7XjbqDug">React Server Components in five minutes</a></strong></em></p></li></ul><h3><strong>Small scenes feel deceptively manageable</strong></h3><p>Most Three.js projects begin with a minimal scene setup: a scene, a camera, a renderer, geometry, and a light source.</p><p>At this stage, the mental model feels clean and predictable. Each component appears isolated and understandable. The renderer draws frames. The camera controls perspective. Geometry defines visible structure. The scene acts as the container that brings everything together.</p><p>A basic setup often looks like this:</p><p><code>const scene = new THREE.Scene();<br><br>const camera = new THREE.PerspectiveCamera(<br>75,<br>window.innerWidth / window.innerHeight,<br>0.1,<br>1000<br>);<br><br>const renderer = new THREE.WebGLRenderer();<br><br>renderer.setSize(<br>window.innerWidth,<br>window.innerHeight<br>);<br><br>document.body.appendChild(<br>renderer.domElement<br>);</code></p><p>At this point, the scene feels conceptually simple because very few systems are interacting simultaneously. The objects are few, the camera is predictable, and the renderer has a clear job: draw what exists in the scene from the camera perspective.</p><p>Then geometry is added. The code still looks approachable because geometry, material, and mesh map neatly to what appears on screen:</p><p><code>const geometry = new THREE.BoxGeometry();<br><br>const material =<br>new THREE.MeshStandardMaterial({<br>color: 0x00ff00<br>});<br><br>const cube = new THREE.Mesh(<br>geometry,<br>material<br>);<br><br>scene.add(cube);</code></p><p>The result feels immediate and satisfying. Something visible exists on screen. The rendering pipeline works. The scene responds correctly.</p><p>This early success is useful, but it can also mislead. Developers often assume that scaling a Three.js project simply means adding more objects or more visual detail. In reality, complexity in 3D scenes rarely grows in a straight line. It compounds through interaction between systems.</p><p>That distinction becomes increasingly important as scenes evolve beyond isolated demos. The first object is usually easy. The ongoing challenge is keeping the scene predictable as more rendering decisions begin depending on one another.</p><h3><strong>Complexity appears through interaction, not object count</strong></h3><p>One of the biggest misconceptions in Three.js development is that complexity scales primarily through object count. Object count matters, but it is not the whole story.</p><p>In practice, complexity scales through interaction between systems. A single additional light source can change material appearance, shadow calculations, rendering cost, scene mood, and spatial readability. A camera adjustment can change composition, movement perception, interaction feel, and depth relationships. An animation update can change render timing, synchronization behavior, responsiveness, and motion continuity.</p><p>This interconnected behavior is what makes seemingly small scenes difficult to maintain. A change that looks isolated at code level may affect how the entire scene feels to the user.</p><p>A simplified dependency chain often looks like this:</p><p><code>Geometry<br>-&gt;<br>Material appearance<br>-&gt;<br>Lighting interaction<br>-&gt;<br>Shadow rendering<br>-&gt;<br>Performance impact<br>-&gt;<br>Perceived scene quality</code></p><p>The important detail is that changes rarely remain localized. A developer may adjust lighting to improve realism, only to discover that materials now appear overly reflective, shadows become visually noisy, scene contrast becomes inconsistent, or frame rate drops unexpectedly.</p><p>The original change was small. The resulting system impact is much larger.</p><p>This is one of the defining characteristics of Three.js applications: systems continuously influence one another. The difficult part is no longer understanding individual features in isolation. The difficult part is understanding how rendering systems behave collectively once the scene becomes more sophisticated.</p><p>That same coordination challenge is starting to shape how developers use AI tools in day-to-day engineering work. The value is not simply asking an assistant to generate code. It is learning how to structure implementation, inspect output, debug issues, and keep the workflow understandable as the project becomes more complex.</p><p>That is the focus of <em><strong><a href="https://www.eventbrite.co.uk/e/hands-on-claude-code-for-beginners-tickets-1989081541435?aff=webdev">Learn Claude Code by Building Live</a></strong></em>, a live hands-on workshop built around practical AI-assisted development workflows. You&#8217;ll see how developers are using Claude Code across coding, debugging, reviewing AI-generated output, managing implementation workflows, and shipping software faster with structured practices.</p><p>Register now with a <strong><a href="https://www.eventbrite.co.uk/e/hands-on-claude-code-for-beginners-tickets-1989081541435?aff=webdev">40% early bird discount using code WEB40</a></strong>.</p><h3><strong>Lighting changes everything</strong></h3><p>Lighting is one of the clearest examples of hidden scene complexity.</p><p>In early Three.js experiments, lighting often feels secondary. Developers focus primarily on geometry because visible objects create immediate visual progress. But lighting determines how the entire scene is perceived.</p><p>Even simple lighting adjustments dramatically alter realism, atmosphere, depth perception, visual hierarchy, and readability. A scene with the same geometry can feel flat, dramatic, realistic, or confusing depending on how light is positioned and balanced.</p><p>For example, adding a directional light initially appears straightforward:</p><p><code>const light =<br>new THREE.DirectionalLight(<br>0xffffff,<br>1<br>);<br><br>light.position.set(5, 10, 7.5);<br><br>scene.add(light);</code></p><p>At first glance, this looks simple. The light has a color, an intensity, and a position. It is added to the scene, and the object becomes easier to read.</p><p>But lighting configuration quickly expands into broader considerations: shadow softness, contrast balance, reflection behavior, intensity calibration, color temperature, and rendering overhead. The complexity becomes more visible once multiple lights interact together.</p><p>Adding ambient light may improve visibility while simultaneously flattening scene depth. Increasing directional light intensity may improve realism while creating harsh contrast in unintended areas. Enabling shadows may improve depth but introduce performance cost.</p><p>This is why lighting rarely behaves like an isolated feature. It becomes part of the overall visual architecture of the scene. As projects mature, developers spend less time asking whether the light works and more time asking whether the lighting supports the experience consistently across the entire scene.</p><h3><strong>Materials introduce hidden rendering complexity</strong></h3><p>Materials often appear straightforward early in development. A developer selects a material type, applies a color or texture, and the object renders correctly. At prototype stage, that feels sufficient.</p><p>But materials are deeply connected to lighting behavior, reflections, transparency, render ordering, texture consistency, and performance characteristics. Material choices determine not only how an object looks, but how that object responds to the rest of the scene.</p><p>A simple material configuration might look like this:</p><p><code>const material =<br>new THREE.MeshPhysicalMaterial({<br>color: 0xffffff,<br>metalness: 0.8,<br>roughness: 0.2<br>});</code></p><p>The complexity is not immediately visible because the material appears correct in isolation. But as scenes evolve, materials begin interacting with multiple light sources, different environment maps, transparency layers, post-processing effects, and varied asset types.</p><p>This creates a coordination problem rather than a simple rendering problem. One object may appear overly reflective under certain lighting conditions. Another may absorb too much light. Texture resolution differences may become visually distracting once assets appear together inside the same scene.</p><p>These are not isolated rendering problems. They are scene consistency problems.</p><p>This is why material systems often become harder to manage over time than geometry itself. Geometry defines structure. Materials define how that structure is visually interpreted, and that interpretation changes continuously depending on the rest of the rendering environment.</p><h3><strong>Animation coordination becomes difficult quickly</strong></h3><p>Animation is another area where hidden complexity emerges gradually.</p><p>A single rotating object feels simple because the animation loop is easy to understand. The frame updates, the object rotates, and the renderer draws the updated scene:</p><p><code>function animate() {<br>requestAnimationFrame(animate);<br><br>cube.rotation.x += 0.01;<br>cube.rotation.y += 0.01;<br><br>renderer.render(scene, camera);<br>}<br><br>animate();</code></p><p>At prototype stage, this loop appears manageable because very little else is competing for synchronization. There is one object, one simple state change, and one render call.</p><p>But animation systems rarely remain isolated. As scenes evolve, animation begins interacting with camera movement, user interaction, lighting updates, physics systems, UI overlays, and asynchronous asset loading. This creates synchronization challenges that are far more difficult than the initial animation setup itself.</p><p>A simplified render relationship often looks like this:</p><p><code>User interaction<br>-&gt;<br>Animation updates<br>-&gt;<br>Scene state changes<br>-&gt;<br>Renderer updates frame<br>-&gt;<br>Camera reflects new state</code></p><p>The difficult part is no longer making objects move. The difficult part is ensuring movement remains coherent across the entire scene.</p><p>Animations that feel smooth individually may conflict once multiple systems update simultaneously. Timing inconsistencies become more noticeable. Interaction latency becomes more visible. Camera movement can make object animation feel too fast or too slow. UI overlays can make scene motion feel visually noisy.</p><p>This is one of the reasons polished 3D applications feel dramatically different from simple demos. The challenge is not merely creating animation. The challenge is coordinating animation behavior across the entire rendering system.</p><h3><strong>Scene organization matters more over time</strong></h3><p>Small Three.js scenes tolerate loose organization. Objects can exist directly inside the scene hierarchy without creating immediate problems. Early prototypes often remain understandable even with minimal structure.</p><p>As projects grow, however, organization becomes increasingly important. Grouping related objects improves transformation management, animation coordination, visibility control, scene readability, and maintainability.</p><p>A simple grouping structure might look like this:</p><p><code>const group = new THREE.Group();<br><br>group.add(mesh1);<br>group.add(mesh2);<br><br>scene.add(group);</code></p><p>Initially, this seems like a small architectural choice. Over time, decisions like this become foundational.</p><p>Without deliberate structure, animation logic becomes fragmented, object relationships become unclear, scene traversal grows more complicated, and interaction systems become difficult to manage.</p><p>The complexity of Three.js scenes often comes less from rendering itself and more from maintaining clarity as systems expand. This is where product-level thinking begins mattering more than individual rendering techniques.</p><h3><strong>Performance problems are usually architectural</strong></h3><p>Performance discussions around Three.js often focus heavily on rendering power. It is easy to assume that performance issues are mainly about the GPU, object count, or whether the browser can draw the scene quickly enough.</p><p>But many performance problems emerge from scene architecture rather than raw rendering capability. Examples include unnecessary object updates, excessive shadow calculations, uncontrolled geometry complexity, redundant render work, and poorly coordinated animation systems.</p><p>These issues accumulate gradually. A scene may perform well initially and degrade slowly as additional systems are introduced. Because the degradation is incremental, the underlying architectural cause is often harder to identify.</p><p>This is why optimization in Three.js is rarely about one dramatic fix. It usually involves improving coordination between systems: reducing unnecessary updates, simplifying lighting interactions, organizing scene hierarchy more effectively, and minimizing expensive rendering calculations.</p><p>The difficult part is not merely making a scene render. The difficult part is maintaining predictable performance as scene complexity grows.</p><h3><strong>Real complexity emerges through accumulation</strong></h3><p>One of the most important characteristics of Three.js development is that complexity compounds incrementally.</p><p>Rarely does a project become difficult because of one major rendering problem. Instead, one additional light is added, another animation loop appears, another material system is introduced, another interaction layer becomes necessary, and another responsive adjustment is required.</p><p>Each decision appears manageable independently. Together, they create an interconnected rendering system that requires significantly more coordination than the original prototype suggested.</p><p>This is why simple demos often feel deceptively complete. The early version demonstrates rendering capability. The mature version requires architectural discipline.</p><h3><strong>The difficult part is maintaining coherence</strong></h3><p>As Three.js scenes evolve, developers increasingly spend time maintaining coherence across rendering behavior, material consistency, animation timing, interaction responsiveness, scene organization, and performance stability.</p><p>This is where the nature of development changes.</p><p>The challenge is no longer:</p><p><em>Can we render this object?</em></p><p>The challenge becomes:</p><p><em>Can we keep the entire scene predictable, performant, and visually coherent as the system grows?</em></p><p>That is a fundamentally different kind of problem.</p><p>It requires structure, restraint, coordination, iterative refinement, and long-term thinking. These are architectural concerns rather than rendering concerns.</p><h3><strong>Key takeaways</strong></h3><ul><li><p>Simple Three.js scenes become complex through interaction between systems, not just object count.</p></li><li><p>Lighting, materials, camera behavior, animation, and performance continuously influence one another.</p></li><li><p>Demos can appear complete before the underlying scene structure is ready for real application growth.</p></li><li><p>Performance issues often emerge from scene organization and repeated updates rather than rendering alone.</p></li><li><p>The long-term challenge is maintaining visual and structural coherence as the scene evolves.</p></li></ul><h3><strong>Conclusion</strong></h3><p>Three.js scenes often appear simple during the early stages of development. A few objects, lights, and animations can quickly create impressive visual output.</p><p>But the hidden complexity of 3D applications emerges through interaction between systems.</p><p>Lighting affects materials. Animations affect rendering behavior. Responsiveness changes composition. Scene hierarchy affects maintainability. Performance depends on coordination across all of them.</p><p>The difficult part is not rendering the first object.</p><p>The difficult part is maintaining coherence as rendering, interaction, lighting, animation, and performance begin influencing one another simultaneously.</p><p>That is where simple scenes stop behaving like demos and start behaving like real applications.</p><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p>&#129366; <strong><a href="https://github.com/oven-sh/bun/pull/30412">Bun&#8217;s million-line leap from Zig to Rust</a></strong>: Bun just made one of the boldest runtime moves we&#8217;ve seen in a while: a 1 million line PR that ports its codebase from Zig to Rust, only days after the branch was described as &#8220;experimental.&#8221; The reason is practical enough. Bun&#8217;s team has been chasing memory leaks and unpredictable crashes, and Rust gives them stronger language-level guardrails for exactly those problems. Still, swapping the foundation of a project this fast is bound to make developers nervous.</p></li><li><p><strong>&#129512; <a href="https://github.com/TanStack/router/issues/7383">TanStack&#8217;s npm compromise shows why fresh installs need a safety buffer</a>: </strong>A malicious npm publish hit 42 @tanstack/* packages this week, with 84 compromised versions live for a short window before being caught. The incident was contained quickly, but it still exposed a real risk for teams running fresh installs in local or CI environments. The practical move is to add friction around brand-new package releases, using safeguards like npm&#8217;s min-release-age, pnpm&#8217;s minimumReleaseAge, and GitHub Actions checks with tools such as zizmor.</p></li><li><p><strong>&#128737;&#65039;<a href="https://vercel.com/changelog/next-js-may-2026-security-release">Next.js patches 13 vulnerabilities that teams should not leave to the firewall</a>: </strong>Next.js shipped a coordinated security release covering 13 vulnerabilities across denial of service, middleware and proxy bypass, SSRF, cache poisoning, and XSS. Several issues cannot be fully mitigated by cloud firewalls, so the recommendation is straightforward: update to 15.5.18 or 16.2.6. There is also a related React Server Functions denial-of-service issue fixed in React RSC packages 19.2.6.</p></li><li><p><strong>&#127959;&#65039; <a href="https://x.ai/news/anthropic-compute-partnership">Anthropic&#8217;s Colossus deal puts AI infrastructure back in the spotlight</a></strong>:<strong> </strong>Anthropic&#8217;s latest compute expansion shows how much frontier AI now depends on access to massive infrastructure. SpaceX and xAI are making Colossus 1 available to Anthropic, giving Claude more capacity as demand grows. The bigger signal is that model competition is no longer only about architecture or benchmarks. It is also about who can secure enough compute, power, and data center capacity to keep products reliable at scale.</p></li><li><p><strong>&#129302; <a href="https://agentfield.ai/github">AgentField turns multiple coding agents into one TypeScript workflow</a></strong>: AgentField is positioning itself as a TypeScript harness for composing Claude Code, Codex, Gemini, and other coding agents into repeatable workflows. The interesting shift is from prompting one assistant at a time to coordinating agents with recipes, constraints, and reusable patterns. For teams experimenting with agentic development, this points toward a more structured way to test what coding agents can actually do together.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p><strong>&#9878;&#65039; <a href="https://read.engineerscodex.com/p/tokenmaxxing-promomaxxing-and-misaligned">Tokenmaxxing asks whether AI is increasing progress or just output</a>: </strong>This piece pushes back on the idea that more AI-generated work automatically means better engineering. Faster coding can help teams explore more ideas, but it can also create more noise, more unfinished features, and more coordination overhead. The useful question is not &#8220;how much can we generate?&#8221; but &#8220;what is worth keeping?&#8221;</p></li><li><p><strong>&#129516; <a href="https://sylvie.fyi/posts/react2shell/">React2Shell shows how curiosity can become an ecosystem-level security story</a>: </strong>This write-up traces the story behind React2Shell, an unauthenticated RCE in React Server Components. The value is not only in the vulnerability itself, but in how the discovery unfolded: curiosity, protocol digging, and a fast-moving disclosure process. It is a useful read for anyone working near modern React infrastructure.</p></li><li><p><strong>&#128269; <a href="https://www.jefftk.com/p/ai-is-breaking-two-vulnerability-cultures">AI is putting pressure on old vulnerability disclosure norms</a>: </strong>Jeff Kaufman looks at how AI changes the balance between quiet security fixes and coordinated disclosure. When AI can scan commits and infer security impact faster, &#8220;silent&#8221; fixes become easier to spot, and long embargoes become harder to manage. The result is a security culture clash that maintainers may not be able to ignore.</p></li><li><p><strong>&#129513; <a href="https://jadjoubran.io/blog/web-platform-influenced-by-libraries">Library patterns keep shaping what the web platform becomes</a>: </strong>This piece looks at how library patterns have influenced the web platform over time. The takeaway is simple: when developers repeatedly solve the same problem in userland, it often points to a missing browser primitive. Today&#8217;s popular library workaround can become tomorrow&#8217;s platform feature.</p></li><li><p><strong>&#9883;&#65039; <a href="https://www.youtube.com/watch?v=5QM7XjbqDug">React Server Components in five minutes</a>: </strong>This short deep dive is a useful companion to the React2Shell discussion because it focuses on the underlying React Server Components model. For developers who want a quick refresher, it helps connect the security conversation back to the architecture: what runs on the server, what reaches the client, and why those boundaries matter so much in modern React apps.</p></li></ul><div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong>Practical AI for Working Developers</strong></h1><p>AI is moving fast, and for a lot of developers, keeping up still feels like learning through trial and error.</p><p><strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com">BuildWithAI</a></strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com"> </a>is Packt&#8217;s newsletter for engineers who want to move beyond AI headlines and start using it in real projects.</p><p>Backed by Packt&#8217;s 7,000+ tech books, courses, and expert resources across 1,000+ technologies, each issue brings you practical workflows, carefully chosen resources, and implementation guidance you can actually apply. Subscribe <strong><a href="https://links.uk.defend.egress.com/Warning?crId=6a01e2d25805d0ca04bfae9f&amp;Domain=packt.com&amp;Threat=eNpzrShJLcpLzAEADmkDRA%3D%3D&amp;Lang=en&amp;Base64Url=eNoFwt0OgiAYANAnQn6ECW3NO0vbavMi8hLwY2oTm9BSn76dM6T0iSeM49fGZNw7c8uMV-jHFVzCROXSCMKQyLlH3FpASjGJnOGeUiHAFX05nWFvaD0t4_3SDEZLVgeSyaNrKxdHg8IMAnzsKs2fk75u4WH35nfo163YilYG8gcadCrY&amp;@OriginalLink=substack.com">here</a></strong>.</p></div><h1 style="text-align: center;"><strong>Tool of the Week</strong></h1><p><strong>&#128000; <a href="https://ratty-term.org/">Ratty makes the terminal weird, visual, and fun again</a></strong></p><p>Ratty is a terminal emulator with inline 3D graphics and a spinning rat cursor. It is playful, strange, and clearly built with a sense of humor, but that is what makes it worth a look. Developer tools do not always need to be purely utilitarian. Sometimes the experiment itself is the point.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item><item><title><![CDATA[WebDevPro #139: The Developer’s Edge in an AI-Assisted Workflow]]></title><description><![CDATA[Welcome to this week&#8217;s issue of WebDevPro!]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-139-the-developers-edge</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-139-the-developers-edge</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 11 May 2026 14:02:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5ICF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to this week&#8217;s issue of WebDevPro! Today&#8217;s piece features insights from <strong><a href="https://www.linkedin.com/in/markjamesprice/">Mark Price</a></strong>, a Microsoft Certified Solutions Developer and former Microsoft Certified Trainer with more than 30 years of experience.</p><p>Mark is also a bestselling author of programming books across .NET, C#, Python, and modern web development,</p><p>Mark brings a practical, developer-first lens to building strong technical foundations. Today&#8217;s piece is drawn from his book <em><strong><a href="https://www.amazon.com/Web-Dev-Sidekick-foundations-hands-ebook/dp/B0GTTXZ9V6">Web Development with an AI Sidekick</a></strong></em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!5ICF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!5ICF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg" width="442" height="293.93" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:800,&quot;resizeWidth&quot;:442,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!5ICF!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0b73072a-8e1e-4a89-bde6-f704f598bd4b_800x532.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>AI has changed the texture of web development work. A few years ago, most of us bounced between docs, Stack Overflow, GitHub issues, and half-finished notes in our own repos. Now, many developers open a chat window first. We ask for an explanation, a starter function, a refactor, a regex fix, or a quick way to debug a failing script. That shift is real, and it is already shaping how people learn and build.</p><p>Still, the real advantage does not come from generating code faster. It comes from using AI without giving away the thinking that makes you a stronger developer.</p><p>That matters most at the intermediate stage. Once you are past the basics, progress stops being about syntax recall alone. The actual challenge is understanding behavior, data flow, failure modes, and the trade-offs that sit behind everyday implementation choices. AI can support that process beautifully. It can also make it easier to skip it.</p><p>The difference comes down to one thing: your mental model.</p><p>Before we get into it, here&#8217;s a sneak peek of this week&#8217;s highlights:</p><ul><li><p>&#128640; <em><strong><a href="https://nodejs.org/en/blog/release/v26.0.0?">Node.js 26 is here with Temporal by default, V8 14.6, and new JavaScript capabilities.</a></strong></em></p></li><li><p>&#127744;<em><strong><a href="https://remix.run/blog/remix-3-beta-preview"> Remix 3 beta feels like a full reset, moving toward a web standards-first framework without React at the center.</a></strong></em></p></li><li><p>&#129517; <em><strong><a href="https://code.visualstudio.com/updates/v1_119">VS Code 1.119 sharpens agent workflows with smoother browser access, telemetry, and sandbox improvements.</a></strong></em></p></li><li><p>&#127760; <em><strong><a href="https://hacks.mozilla.org/2026/05/trustworthy-javascript-for-the-open-web/">Mozilla makes the case for trustworthy JavaScript and stronger guarantees around delivered browser code.</a></strong></em></p></li><li><p>&#9201;&#65039; <em><strong><a href="https://dev.to/lazarv/time-to-yield-20m8">Time to Yield revisits frontend responsiveness and why performance is also about keeping the interface alive.</a></strong></em></p></li><li><p>&#10024; <em><strong><a href="https://animejs.com/">Anime.js keeps web animation approachable with timelines, SVG animation, scroll effects, and draggable interactions.</a></strong></em></p></li></ul><h3><strong>JavaScript still lives or dies on behavior</strong></h3><p>In frontend work, JavaScript remains the place where user behavior becomes actual application behavior. A click updates a count. A form input changes local state. A toggle shows hidden content. A validation rule decides whether something can be submitted. These are ordinary interactions, but they expose the central challenge of frontend development: the browser is always reacting to changing state.</p><p>That is why JavaScript becomes harder long before it becomes &#8220;advanced.&#8221; The difficulty usually is not the syntax. It is the chain of cause and effect. One event triggers a function. That function updates a variable. The UI reads that value and renders it. Another function depends on that same value later. At that point, you are no longer writing isolated lines of code. You are managing behavior.</p><p><code>const button = document.querySelector(&#8217;#save&#8217;);<br>const status = document.querySelector(&#8217;#status&#8217;);<br><br>button.addEventListener(&#8217;click&#8217;, async () =&gt; {<br>status.textContent = &#8216;Saving...&#8217;;<br><br>try {<br>await saveDraft();<br>status.textContent = &#8216;Saved successfully&#8217;;<br>} catch (error) {<br>status.textContent = &#8216;Something went wrong&#8217;;<br>console.error(error);<br>}<br>});</code></p><p>This is a small example, but it shows the real shape of frontend work. The question is not just &#8220;does it run?&#8221; The question is &#8220;what should the user see while something is happening, and what should happen when it fails?&#8221;</p><p>That is also where AI can be genuinely useful. I find it most helpful when it is asked to explain behavior, not just produce output. If you ask it, &#8220;Why might this click handler fail silently?&#8221; or &#8220;What edge cases should I think about here?&#8221; you get much better value than you do from &#8220;write this feature for me.&#8221;</p><p>The browser still rewards developers who can reason clearly about state, events, and side effects. AI helps, but it does not remove that requirement.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!WlRx!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 424w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 848w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!WlRx!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png" width="835" height="195" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:195,&quot;width&quot;:835,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 424w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 848w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 1272w, /__u/substackcdn.com/image/fetch/$s_!WlRx!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcee25bf9-bb8e-4739-a38d-f3a581fdb97c_835x195.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h3><strong>Debugging is still a first-class skill</strong></h3><p>One quiet risk with AI-generated code is that it can make clean-looking code feel trustworthy before it has earned that trust.</p><p>A function can look polished and still rely on DOM elements that do not exist. A promise can be written cleanly and still swallow an error. A generated solution can solve the visible symptom while leaving the original logic problem untouched. That is why debugging remains one of the most important skills in modern web work.</p><p>The console is still your friend. So are breakpoints, network inspection, and reading stack traces without panic. AI can help interpret an error, but the developer still needs to inspect the real runtime conditions.</p><p><code>const form = document.querySelector(&#8217;#signup-form&#8217;);<br><br>console.log(&#8217;Form found?&#8217;, !!form);<br><br>form?.addEventListener(&#8217;submit&#8217;, (event) =&gt; {<br>event.preventDefault();<br>console.log(&#8217;Form submitted&#8217;);<br>});</code></p><p>This snippet is simple, but it reflects a useful habit: check your assumptions in the environment where the code actually runs.</p><p>That habit matters more in AI-assisted workflows. If you paste an error into a chatbot before you inspect the actual state of the page, you risk outsourcing the wrong question. The better sequence is this: reproduce the issue, inspect the DOM or runtime values, form a hypothesis, then ask AI to help test or refine that hypothesis.</p><p>That turns AI into a debugging partner rather than a guess machine.</p><h3><strong>TypeScript makes fuzzy assumptions visible</strong></h3><p>JavaScript gives you freedom. TypeScript pushes back when that freedom becomes vague.</p><p>That is why TypeScript matters even for developers who already &#8220;know JavaScript.&#8221; Its biggest benefit is not prestige or complexity. It is visibility. It reveals where your assumptions are too loose.</p><p>If a user may not have a profile image, the code should say so. If a function returns either success data or an error object, the code should say so. If a property can only be one of three allowed strings, the code should say so.</p><p>That sounds obvious, but teams often carry these assumptions informally. AI-generated code tends to make that worse because it often fills in the blanks with something plausible rather than something verified.</p><p><code>type SurveyStatus = &#8216;draft&#8217; | &#8216;published&#8217; | &#8216;closed&#8217;;<br><br>interface Survey {<br>id: number;<br>title: string;<br>status: SurveyStatus;<br>responseCount?: number;<br>}<br><br>function getStatusLabel(survey: Survey): string {<br>if (survey.status === &#8216;draft&#8217;) return &#8216;Work in progress&#8217;;<br>if (survey.status === &#8216;published&#8217;) return &#8216;Live now&#8217;;<br>return &#8216;No longer accepting responses&#8217;;<br>}</code></p><p>This is not flashy code, but it is valuable code. It makes the shape of the data easier to understand at a glance. It also gives your editor a chance to protect you from sloppy assumptions.</p><p>This is where AI becomes much more effective, too. When you prompt with clear data shapes, expected states, and explicit constraints, the quality of the generated response improves immediately. Good prompts in development often look a lot like good system design. They define what is allowed, what is optional, and what should happen when something is missing.</p><p>Developers sometimes treat TypeScript as overhead because the application seems small. The problem is that applications rarely stay small in the ways that matter. They grow in decisions, edge cases, and invisible dependencies. TypeScript helps bring those decisions into the open.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ATpL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 424w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 848w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ATpL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png" width="1430" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1430,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 424w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 848w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ATpL!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb4b518bb-cc4d-4848-9ebc-52866c980b5b_1430x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Backend work rewards structure earlier than people expect</strong></h3><p>On the backend, the same pattern appears in a different form. It is easy to write a Python script that works once. It is much harder to build backend logic that stays understandable after a few rounds of changes, debugging, and feature growth.</p><p>That is why the backend conversation should not just be about language choice or framework speed. It should be about structure. Small functions, sensible modules, meaningful variable names, clear error handling, and isolated responsibilities all matter much earlier than most people expect.</p><p><code>def calculate_completion_rate(total_responses, completed_responses):<br>if total_responses == 0:<br>return 0<br><br>return round((completed_responses / total_responses) * 100, 2)</code></p><p>A tiny function like this is not interesting because it is clever. It is useful because it is readable, testable, and easy to reuse. That is often the better way to judge backend code quality. Not &#8220;How compact is it?&#8221; but &#8220;Can I understand it six weeks later?&#8221;</p><p>AI can help a lot here. It can suggest cleaner names, extract a helper function, explain a traceback, or show a better way to handle a repeated block of logic. Still, the best outcomes come when you already know what kind of structure you want.</p><p>If you ask AI to &#8220;write backend code for analytics,&#8221; you may get something that works. If you ask it to &#8220;separate data access, business logic, and formatting concerns,&#8221; you are much more likely to get something maintainable. That difference matters.</p><h3><strong>Good AI usage looks more like collaboration than delegation</strong></h3><p>There is a lot of talk about prompt engineering, but for most working developers, the real skill is much simpler: knowing how to ask better technical questions.</p><p>Strong AI-assisted development usually includes some version of the following habits:</p><ul><li><p>asking for an explanation before asking for a solution</p></li><li><p>requesting alternatives and trade-offs, not just one answer</p></li><li><p>giving the model the data shape, edge cases, and constraints up front</p></li><li><p>reviewing generated code line by line before accepting it</p></li><li><p>testing in the real environment, not just trusting a neat-looking response</p></li></ul><p>Those habits keep you in the driver&#8217;s seat.</p><p>Bad prompt:<br><em>&#8220;<strong>Write a form validation function.</strong>&#8220;</em><br><br>Better prompt:<br><em>&#8220;<strong>Write a TypeScript function that validates a survey form.</strong></em><br><em><strong>Fields: title (required, min 5 chars), email (optional, valid format if present),</strong></em><br><em><strong>questions (must contain at least 1 item).</strong></em><br><em><strong>Return an object with field-specific error messages.</strong>&#8220;</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!u4sI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 424w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 848w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 1272w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!u4sI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png" width="1430" height="805" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:805,&quot;width&quot;:1430,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 424w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 848w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 1272w, /__u/substackcdn.com/image/fetch/$s_!u4sI!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F94a20bfc-dfa9-4349-ba3e-92bcc9cc6c15_1430x805.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>The real pattern is bigger than any one language</strong></h3><p>What I find most interesting is that the same deeper ideas keep showing up across the stack.</p><p>In JavaScript, you are reasoning about user interaction and state changes. In TypeScript, you are making assumptions explicit so those changes stay manageable. In Python, you are organizing logic so that behavior can scale without becoming confusing. Different syntax, same underlying discipline.</p><p>That is why AI works best for developers who are trying to strengthen their mental model, not bypass it.</p><p>A developer with a clear mental model asks better questions:</p><ul><li><p>What state is changing here?</p></li><li><p>What data shape does this function expect?</p></li><li><p>What happens when the input is missing or invalid?</p></li><li><p>Where should this responsibility live?</p></li><li><p>How should the system fail?</p></li><li><p>Is this solution easier to maintain than the last one?</p></li></ul><p>Those questions improve your code even before AI answers them. Then AI becomes useful in the right way. It helps you refine, test, explore, and debug. It stops being a code vending machine and starts acting more like a second pair of eyes.</p><h3><strong>Takeaways</strong></h3><p>AI is now part of the everyday web development toolkit, and that is not changing. The developers who benefit most will not be the ones who hand over every task blindly. They will be the ones who use AI to sharpen their understanding of the stack.</p><p>JavaScript still demands a clear grasp of browser behavior. TypeScript still rewards developers who make assumptions explicit. Python still works best when logic is structured early and cleanly. Across all three, the strongest habit is the same: treat generated output as something to interrogate, not something to trust by default.</p><p>That is the mindset worth building.</p><p>AI can speed up the path to a solution. Your mental model determines whether the solution actually holds up.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!ovE3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 424w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 848w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!ovE3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png" width="350" height="431.74342105263156" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1500,&quot;width&quot;:1216,&quot;resizeWidth&quot;:350,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 424w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 848w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 1272w, /__u/substackcdn.com/image/fetch/$s_!ovE3!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa711c7b6-6e09-4e2c-8cee-eaaad973c844_1216x1500.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>If this way of thinking about AI-assisted development resonates with you, <strong>Web Dev with an AI Sidekick</strong> builds on the same idea in a more structured, practical way. You can pre-order the book on Amazon here: <strong><a href="https://www.amazon.com/Web-Dev-Sidekick-foundations-hands/dp/180611125X?utm_source=chatgpt.com">Web Dev with an AI Sidekick</a></strong></em></p><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!uqKv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 424w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 848w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!uqKv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png" width="514" height="257" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:514,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 424w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 848w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 1272w, /__u/substackcdn.com/image/fetch/$s_!uqKv!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F857b7ee0-012c-404b-8f9c-687434c2ada3_1880x940.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;"><strong>Join Anna J McDougall, Field CTO at HashiCorp / IBM, for a live workshop on how engineering careers are evolving in the AI era.</strong></p><p style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/career-paths-as-a-developer-in-the-ai-era-tickets-1987116539060?aff=newsletterinternal&amp;discount=innl40">&#128197; May 30 | Live Online Workshop</a></strong></p><p style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/career-paths-as-a-developer-in-the-ai-era-tickets-1987116539060?aff=newsletterinternal&amp;discount=innl40">Register Now</a></strong></p><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p><strong>&#128640; <a href="https://nodejs.org/en/blog/release/v26.0.0?utm_source=chatgpt.com">Node.js 26 lands as the new Current release</a>: </strong>Node.js 26 is here, with the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal">Temporal API</a> enabled by default, V8 14.6, and Undici 8. Developers also get new JavaScript capabilities such as <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert">Map.prototype.getOrInsert()</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/concat">Iterator.concat()</a>. This is the cutting-edge Current release until October, when Node.js 26 is expected to move to LTS. It is a good time to test compatibility, explore the runtime changes, and see what might affect production workflows later this year.</p></li><li><p><strong>&#127744; <a href="https://remix.run/blog/remix-3-beta-preview?utm_source=chatgpt.com">Remix 3 beta preview takes a sharper turn</a>: </strong>Remix has had a long journey: it was created by the team behind <a href="https://reactrouter.com/">React Router</a>, positioned as an <a href="https://nextjs.org/">alternative to Next.js</a>, acquired by <a href="https://www.shopify.com/">Shopify</a>, and later folded into React Router v7. Now Remix 3 is stepping out in a new direction: a full-stack, web standards-first framework with its own UI component model and no React at the center. It feels less like a version bump and more like a reset.</p></li><li><p><strong>&#129514;<a href="https://news.ycombinator.com/item?id=48016880&amp;utm_source=chatgpt.com">A Bun experiment turns into a bigger debate</a></strong>:<strong> </strong>Jarred Sumner added a <a href="https://github.com/oven-sh/bun">Zig to Rust porting guide</a> to the Bun repo, and Hacker News quickly turned it into a much larger conversation about Bun&#8217;s future. The speculation got intense enough that Jarred stepped in and said the whole thread was an overreaction. It is a small reminder of how closely developers watch fast-moving tools. Even an experiment can look like a strategy shift when the project has this much attention.</p></li><li><p>&#129517; <strong><a href="https://code.visualstudio.com/updates/v1_119?utm_source=chatgpt.com">VS Code 1.119 sharpens agent workflows</a></strong>: VS Code 1.119 focuses on smoother agent interactions, better observability, and fewer workflow interruptions. Agents can now request access to shared browser tabs, Copilot Chat sessions can emit <a href="https://opentelemetry.io/">OpenTelemetry</a> traces, and sandboxed agents get more practical network and temporary-file permissions. It is another step toward making AI coding agents feel less separate from the editor.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p><strong>&#128272; <a href="https://mobeigi.com/blog/security/security-through-obscurity-is-not-bad/?utm_source=chatgpt.com">Security through obscurity deserves a better conversation</a>: </strong>This piece challenges the usual dismissal of &#8220;security through obscurity.&#8221; The argument is not that obscurity should replace strong security, but that it can still be useful as one extra layer. It is a thoughtful read for anyone who has seen good security advice turn into rigid slogans.</p></li><li><p><strong>&#129391; <a href="https://wwj.dev/posts/i-am-worried-about-bun/">A developer worries about Bun&#8217;s future</a>: </strong>This post looks at Bun with both admiration and concern. The runtime is fast, ambitious, and widely loved, but its future now carries bigger questions around ownership, priorities, and long-term stewardship. The piece is a developer asking what happens when a tool people rely on becomes part of a much larger company story.</p></li><li><p><strong>&#127760; <a href="https://hacks.mozilla.org/2026/05/trustworthy-javascript-for-the-open-web/?utm_source=chatgpt.com">Mozilla makes the case for trustworthy JavaScript</a>: </strong>Mozilla explores how web apps can make their delivered JavaScript more trustworthy. The core concern is simple: users need a way to know that the code running in the browser is the code developers intended to ship. This matters most for high-trust apps, especially where privacy and encryption are involved. The open web needs stronger ways to make invisible tampering harder to hide.</p></li><li><p><strong>&#127909; <a href="https://www.youtube.com/watch?v=PX3QlADinIE">A useful watch for your developer queue</a>: </strong>I would keep this as a short watchlist item unless you want to share the title or main theme. Without that, the safest version is simple and curiosity-led. This video is worth saving for a slower watch, especially if you like technical talks that connect engineering decisions with the way developers actually build, debug, and maintain software.</p></li><li><p><strong>&#9201;&#65039;<a href="https://dev.to/lazarv/time-to-yield-20m8?utm_source=chatgpt.com">Time to Yield revisits frontend responsiveness</a>: </strong>This piece looks at yielding and responsiveness from a practical frontend angle. It is less about chasing benchmark wins and more about how JavaScript work affects the user&#8217;s experience. That framing is useful because performance is not only about speed. It is also about when work happens, how much room the browser gets, and whether the interface still feels alive under pressure.</p></li></ul><h1 style="text-align: center;"><strong>Practical AI for Working Developers</strong></h1><p>AI is moving fast, and for a lot of developers, keeping up still feels like learning through trial and error.</p><p><strong><a href="/__u/packtbuildwithai.substack.com/">BuildWithAI</a></strong><a href="/__u/packtbuildwithai.substack.com/"> </a>is Packt&#8217;s newsletter for engineers who want to move beyond AI headlines and start using it in real projects.</p><p>Backed by Packt&#8217;s 7,000+ tech books, courses, and expert resources across 1,000+ technologies, each issue brings you practical workflows, carefully chosen resources, and implementation guidance you can actually apply. Subscribe <a href="/__u/packtbuildwithai.substack.com/">here</a>.</p><h1 style="text-align: center;"><strong>Tool of the Week</strong></h1><p><strong><a href="https://animejs.com/">Anime.js keeps web animation approachable</a> &#10024;</strong></p><p>Anime.js gives developers a polished way to build expressive motion on the web, from timelines and SVG animation to scroll-triggered effects and draggable interactions. It is useful when animation needs to feel intentional rather than decorative. The API stays approachable, but there is enough depth for teams building more crafted, interactive interfaces.</p><p><strong><a href="https://crashcat.dev/examples/#example-shapes">Crashcat makes physics demos surprisingly fun</a> &#128049;</strong></p><p>Crashcat is a JavaScript 3D rigid body physics library for games, simulations, and interactive web experiences. It also has the kind of homepage you remember: a cat in a convertible driving through endless obstacles.</p><p>That playful first impression works. The examples make the library feel inviting before the technical details take over.</p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://packtwebdevpro.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Web Development Newsletter by Packt! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[WebDevPro #138: The agentic coding loop: PRD → task planner → parallel agents]]></title><description><![CDATA[By Alexandre Zajac]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-138-the-agentic-coding</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-138-the-agentic-coding</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 04 May 2026 14:02:28 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4zgw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><h1 style="text-align: center;"><strong><a href="https://www.vpdae.com/redirect/7au88233yznh73di5miqvjri992">Catch the latest HubSpot Developer Platform updates in Spring Spotlight</a></strong></h1><p>Spring Spotlight 2026 is live and we&#8217;ve rounded up the top updates for developers. See what&#8217;s new for the HubSpot Developer Platform!</p><p>Ship faster with AI coding tools like Cursor, Claude Code, and Codex. Build MCP-powered AI connectors, run serverless functions with support for UI extensions, and use date-based versioning to streamline roadmap planning.</p><p style="text-align: center;"><strong>Explore Now</strong></p></div><div><hr></div><p>Welcome to this week&#8217;s issue of WebDevPro!</p><p>Today&#8217;s piece comes from <strong><a href="https://go.hungryminds.dev/linkedin">Alexandre Zajac</a></strong>, an engineer from Prime Video who&#8217;s been thinking deeply about how AI coding workflows actually behave in the real world.</p><p>Alex doesn&#8217;t just experiment at the surface level; he digs into the friction points, the failure modes, and the patterns that emerge when you push agents beyond toy problems.</p><p>He built this <em><strong><a href="https://go.hungryminds.dev/agentic-loop-starter-kit">PDF starter kit</a></strong></em> to help you go through it!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4zgw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 424w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 848w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4zgw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png" width="216" height="267.84" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:620,&quot;width&quot;:500,&quot;resizeWidth&quot;:216,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 424w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 848w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4zgw!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3680c7a2-bb86-4af0-8c3e-5e588cc4dc14_500x620.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In this article, he walks through a practical system for making agentic coding reliable, drawing from firsthand experience and real constraints. It&#8217;s thoughtful, grounded, and immediately useful if you&#8217;ve ever felt your AI sessions spiral after a few dozen turns.</p><p>Before we get into it, here&#8217;s a sneak peek of this week&#8217;s highlights:</p><ul><li><p><em>&#129504; <a href="https://poolside.ai/models">A new contender just entered the coding model race</a></em></p></li><li><p><em>&#9881;&#65039; <a href="https://cursor.com/blog/typescript-sdk">Cursor just turned its agents into an API</a></em></p></li><li><p><em>&#9883;&#65039; <a href="https://saschb2b.com/blog/react-compiler-year-in-review">React is still trying to automate performance</a></em></p></li><li><p><em>&#128994; <a href="https://github.com/nodejs/node/pull/62526">Node 26 hit a delay, but Temporal is still close</a></em></p></li><li><p><em>&#129504; <a href="https://openai.com/index/where-the-goblins-came-from">Where the goblins in ChatGPT actually came from</a></em></p></li><li><p><em>&#129513; <a href="https://openai.com/index/open-source-codex-orchestration-symphony/">Coding is shifting from prompts to orchestration</a></em></p><div><hr></div></li></ul><p>&#128075; Hi! It&#8217;s <a href="https://go.hungryminds.dev/linkedin">Alex</a>!</p><p>Let&#8217;s imagine this scene. You open a new chat. You describe the feature.<br>For twenty minutes, the AI is cooking.<br>Good code, too.</p><p>Around turn 30, it starts fixing a bug it introduced three turns ago.<br>It imports a module you deleted. It references a file path you renamed ten messages back.</p><p>You&#8217;re not building anymore. You&#8217;re managing.</p><p>Feeding corrections back in, watching the agent regenerate the same broken pattern with different variable names.</p><p>This is the death loop, and it&#8217;s not a prompt quality problem.<br>Session length is the variable that matters.</p><p>I created a simple system to ship simple changes with agents (almost) autonomously, and in this article, I want to show the why and how I built it.</p><h3><strong>Context erosion</strong></h3><p>The<a href="https://anthropic.com/engineering/claude-code-best-practices"> Anthropic Claude Code docs</a> say it directly: a single debugging session can generate tens of thousands of tokens, and when the context window fills up, Claude starts forgetting earlier instructions and making more mistakes.</p><p>A 10-turn session is fine.<br>A 50-turn session drifts.<br>At 80 turns, you&#8217;re spending more time correcting than building, and at that point, you might as well have written the code yourself.</p><p><a href="https://martinfowler.com/articles/exploring-gen-ai/13-role-of-developer-skills.html">Birgitta B&#246;ckeler</a>, writing in the Martin Fowler engineering series, said it after months of daily agentic coding: the longer a session gets, the more hit-and-miss it becomes, regardless of the rigor in prompting.</p><h3><strong>Assumption stacking</strong></h3><p>Agents make assumptions about your codebase.<br>Most are correct.</p><p>When one is wrong and goes unchallenged, the agent builds on it. Then you build on the agent&#8217;s code. Three levels in, the wrong assumption is load-bearing.</p><p>Documented example from the<a href="https://martinfowler.com/articles/exploring-gen-ai/13-role-of-developer-skills.html"> same Martin Fowler series</a>: an agent diagnosed a Docker build failure as an architecture mismatch and changed the Docker settings. The actual cause was node_modules built for the wrong platform. Classic error. Any developer who&#8217;s been burned by it once would catch it in 30 seconds. Without that catch, the agent would have spent hours deepening a wrong diagnosis into increasingly creative fixes.</p><p>Agents are <strong>confidently wrong</strong> with no built-in correction mechanism.</p><h3><strong>Filesystem collisions</strong></h3><p>Two agents, one working directory:</p><ul><li><p><strong>agent-1</strong>:</p></li></ul><p><code>git add . &amp;&amp; git commit -m &#8220;fix auth middleware&#8221;</code></p><ul><li><p><strong>agent-2:</strong></p></li></ul><p><code>git add . &amp;&amp; git commit -m &#8220;refactor API layer&#8221;</code></p><p># agent-2 just committed agent-1&#8217;s half-finished auth changes</p><p>Every agent sees every other agent&#8217;s uncommitted changes.<br>Files get overwritten mid-edit.<br>One agent&#8217;s npm install blows away another&#8217;s node_modules.</p><p>Commits become incoherent.</p><p>Open two Cursor tabs on the same repo and give it 20 minutes. Productive at first. Then a mess that takes longer to untangle than doing the work sequentially.</p><p>So how do we make this consistently okay?</p><h3><strong>Stage 1: The PRD</strong></h3><p>A PRD is not a 47-page corporate artifact. It&#8217;s a short document you write before opening your coding tool. Its job: prevent assumption stacking before the agent gets a chance to start.</p><p>I always use these for medium-to-large features.</p><p>It&#8217;s similar to the <a href="https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/">Harper Reed workflow</a> published in February 2025.</p><p>It got traction because it was concretely different from how most people worked at the time. His first move is a conversational spec session where an LLM asks him one question at a time until the idea is fully elaborated.</p><p>The prompt:</p><p>&#8220;<em><strong>Ask me one question at a time so we can develop a thorough,</strong></em></p><p><em><strong>step-by-step spec for this idea. Each question should build</strong></em></p><p><em><strong>on my previous answers, and our end goal is to have a detailed</strong></em></p><p><em><strong>specification I can hand off to a developer. Let&#8217;s do this</strong></em></p><p><em><strong>iteratively and dig into every relevant detail. Remember,</strong></em></p><p><em><strong>only one question at a time.&#8221;</strong></em></p><p><em><strong>Here&#8217;s the idea: [IDEA]</strong></em></p><p>When the questions run dry:</p><p><em><strong>&#8220;Now that we&#8217;ve wrapped up the brainstorming process, can you</strong></em></p><p><em><strong>compile our findings into a comprehensive, developer-ready</strong></em></p><p><em><strong>specification? Include all relevant requirements, architecture</strong></em></p><p><em><strong>choices, data handling details, error handling strategies,</strong></em></p><p><em><strong>and a testing plan so a developer can immediately begin</strong></em></p><p><em><strong>implementation.&#8221;</strong></em></p><p>Output goes to spec.md. The whole thing takes 15 minutes.</p><p>The<a href="https://www.runpane.com/blog/ai-native-development-workflow"> Doozy founders shipped a 300k-line Next.js monorepo</a> with 3 to 6 parallel agents at any given time. They do it differently. They built a /discussion command that runs before any code is written. Subagents explore the codebase and look up dependencies. The command produces no edits, only a written summary in .context/context.md. Other agents reference that file.</p><h4><strong>What goes in the spec</strong></h4><p><em><strong># spec.md</strong></em></p><p><em><strong>## Problem</strong></em></p><p><em><strong>What you&#8217;re building and why, in one sentence.</strong></em></p><p><em><strong>## Constraints</strong></em></p><p><em><strong>What the agent should NOT touch.</strong></em></p><p><em><strong>What patterns to follow.</strong></em></p><p><em><strong>## Files to read</strong></em></p><p><em><strong>src/auth/middleware.ts</strong></em></p><p><em><strong>src/routes/users.ts</strong></em></p><p><em><strong>prisma/schema.prisma</strong></em></p><p><em><strong>## Definition of done</strong></em></p><p><em><strong>npm run typecheck &amp;&amp; npm test passes with no new failures.</strong></em></p><p>150 to 300 words. The format matters less than writing it before you start prompting.</p><p>One test: if you can&#8217;t describe what the feature should <em>not</em> do, keep writing.</p><p>Where it lives: spec.md in the repo root, a context section in CLAUDE.md, or .context/context.md. Pick whatever your tool loads by default.</p><p>Watch out for CLAUDE.md files that balloon. The<a href="https://code.claude.com/docs/en/best-practices"> Claude Code docs</a> are explicit about this: bloated CLAUDE.md files cause Claude to ignore your actual instructions. Treat it like code. Prune it. If you added a rule and Claude&#8217;s behavior didn&#8217;t change, the rule is noise. Delete it.</p><h3><strong>Stage 2: The task planner</strong></h3><p>The task planner converts a spec into a sequenced list of bounded tasks.<a href="https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/"> Harper Reed</a> uses a reasoning model for this step, and I do too. Not for code, just for decomposition.</p><p>A possible prompt:</p><p><em><strong>Draft a detailed, step-by-step blueprint for building this project.</strong></em></p><p><em><strong>Then break it down into small, iterative chunks that build on each</strong></em></p><p><em><strong>other. Go another round to break it into small steps. Review and</strong></em></p><p><em><strong>make sure the steps are small enough to implement safely, but big</strong></em></p><p><em><strong>enough to move the project forward.</strong></em></p><p>Make sure each prompt builds on the previous prompts, and ends with wiring things together. There should be no hanging or orphaned code that isn&#8217;t integrated into a previous step.</p><p>[SPEC]</p><p>Output goes to prompt_plan.md. Then he asks for a todo.md checklist the execution agent can check off. That&#8217;s how state persists across sessions without re-reading the full conversation history.</p><p><a href="https://www.anthropic.com/research/building-effective-agents">Anthropic describes this as the orchestrator-workers pattern</a>: a central LLM breaks down tasks, delegates them to worker LLMs, and synthesizes results. The reasoning is that you can&#8217;t always predict the subtasks, especially in code, where the number of files and the nature of changes depend on the task.</p><h4><strong>What &#8220;agent-sized&#8221; means</strong></h4><p>A task is agent-sized if it fits in one context window, produces a green lint/typecheck, and has a binary done/not-done signal.</p><p>These will send your agent spiraling:</p><p><strong>&#10060;</strong><em><strong> &#8220;Refactor the authentication system&#8221;</strong></em></p><p><strong>&#10060;</strong><em><strong> &#8220;Add tests&#8221;</strong></em></p><p><strong>&#10060;</strong><em><strong> &#8220;Improve performance&#8221;</strong></em></p><p>These work:</p><p><strong>&#9989; </strong><em><strong>&#8220;Add a useAuth hook to src/hooks/auth.ts that wraps the</strong></em></p><p><em><strong>existing authService and exposes login, logout, and</strong></em></p><p><em><strong>isAuthenticated. Update src/components/LoginButton.tsx</strong></em></p><p><em><strong>to use it. Done when: npm run typecheck passes.&#8221;</strong></em></p><p><strong>&#9989;</strong> <em><strong>&#8220;Add a POST /api/users/:id/preferences endpoint to</strong></em></p><p><em><strong>src/routes/users.ts that validates the body against</strong></em></p><p><em><strong>UserPreferencesSchema and writes to user_preferences.</strong></em></p><p><em><strong>Done when: the new route test passes.&#8221;</strong></em></p><p>Specific file. Specific interface. Specific check.</p><p>The<a href="https://www.runpane.com/blog/ai-native-development-workflow"> Pane team</a> scores every plan on a 1-10 confidence scale for one-pass implementation success. Below 8, iterate. Their rules: no aspirations, only instructions. No open questions (if something is unresolved, stop and research first). And if a plan is too big for one session, split it. Oversized plans are where the death loop starts.</p><h3><strong>Stage 3: Parallel agents</strong></h3><p>Here are the 3 concepts I use the most to make parallel agents work.</p><p>Once you have a dependency-ordered task list, tasks that don&#8217;t share files can run at the same time. The thing that used to block this was filesystem collisions. The fix has been sitting in<a href="https://git-scm.com/docs/git-worktree"> git since 2015</a>.</p><h4><strong>Git worktrees</strong></h4><p>One command. Sub-second. The new directory is a fully independent working tree on its own branch, sharing the same .git object store. No re-cloning. Disk cost is just for your working files.</p><p><code>git worktree add .worktrees/feature-auth -b session/feature-auth</code></p><p>Each agent gets a clean branch, zero visibility into other agents&#8217; uncommitted changes, and full ability to run tests independently.</p><p>When done:</p><p><code>git worktree remove --force .worktrees/feature-auth</code></p><p>The branch stays if there are commits to review. Otherwise, it&#8217;s gone.</p><p>This primitive from 2015 that nobody cared about until this year now underpins several independent tools:</p><ul><li><p><strong><a href="https://github.com/mixpeek/amux">amux</a></strong> runs up to 30 Claude Code agents in parallel. Uses SQLite compare-and-swap for atomic task claiming. No Redis. No Kubernetes. Just a WHERE clause and SQLite&#8217;s write lock.</p></li><li><p><strong><a href="https://github.com/generalaction/emdash">Emdash</a></strong> (YC W26) is an open-source desktop app supporting 20+ CLI providers, including Claude Code, Codex, and Gemini CLI, with direct integration into Linear, GitHub, and Jira, plus SSH to remote machines.</p></li><li><p><strong><a href="https://github.com/Dcouple-Inc/Pane">Pane</a></strong> is open-source, from the Doozy founders. Same team behind the 300k-line monorepo. Cross-platform, agent-agnostic, keyboard-first.</p></li></ul><p>All of these solutions are great, but in reality, to get started, none of them are required.<br>A task file and a terminal per worktree gets it done.</p><h4><strong>Scoping agents</strong></h4><p>Every agent session starts with the task description from the plan, the specific files listed in the task, and the relevant section of the spec.</p><p><em><strong># Task: Add useAuth hook</strong></em></p><p><em><strong>Files to read:</strong></em></p><p><em><strong>- src/services/authService.ts (existing service to wrap)</strong></em></p><p><em><strong>- src/hooks/ (existing hook patterns to follow)</strong></em></p><p><em><strong>- src/components/LoginButton.tsx (file to update)</strong></em></p><p><em><strong>Constraints: Do not modify authService.ts.</strong></em></p><p><em><strong>Done: npm run typecheck passes, LoginButton uses useAuth.</strong></em></p><p>The agent can&#8217;t drift into adjacent work because adjacent work isn&#8217;t in its context. It either completes the task or fails clearly.</p><p>Ten 20-turn sessions instead of one 200-turn session.</p><h3><strong>Integration checks</strong></h3><p>TypeScript strict mode, npm run typecheck, and lint are your coordination layer across parallel agents. An agent&#8217;s work isn&#8217;t done until the checks pass. This prevents a type error in one worktree from silently propagating when branches merge. This scales across languages.</p><p>Try to enforce this per task: implement, typecheck, lint, format, and fix all issues before proceeding. After all tasks finish, a reviewer subagent reruns everything and verifies that the plan was completed.</p><p>Human review stays in the loop for architectural decisions, ambiguous acceptance criteria, and code that&#8217;s syntactically valid but semantically wrong. Automated checks catch everything describable as a rule. They don&#8217;t catch everything.</p><h3><strong>What this still doesn&#8217;t fix</strong></h3><p>Agents still hallucinate library APIs. They&#8217;ll write syntactically correct code against an API that changed in the last major version. The planning stage reduces this by including research tasks with URLs, but it doesn&#8217;t eliminate it. Verify unfamiliar library usage against docs before shipping.</p><p>The spec quality ceiling is your knowledge. If you don&#8217;t understand your codebase well enough to write a clear spec, the agent won&#8217;t either. The planning phase surfaces this. If you can&#8217;t finish the plan without open questions, you need to learn more before you start.</p><p>Merge conflicts still happen when two tasks share a file the plan didn&#8217;t account for. Worktrees stop filesystem collisions. They don&#8217;t do design work.</p><p>The tooling layer is moving fast.<a href="https://git-scm.com/docs/git-worktree"> Git worktrees</a> are stable. The orchestration tools built on top of them aren&#8217;t. Build your workflow around git worktree add, not the wrapper tool.</p><p>Context pollution goes both ways. Too little context and the agent hallucinates your conventions. Too much, and the real rules get buried. The same principle applies to task files: dense, explicit, and short.</p><p>Entropy compounds. Incomplete refactors, mixed conventions, dead code. All of it degrades agent performance on the next feature because the agent has to reason about a messier codebase. This workflow doesn&#8217;t fix that.</p><h3><strong>Where to start this week</strong></h3><p>Don&#8217;t adopt all three stages at once.</p><p>Start with Stage 2. Before your next AI coding session, spend 10 minutes writing a task plan. Three things:</p><ol><li><p>Which files does this touch (name them)</p></li><li><p>What it should NOT do (scope the blast radius)</p></li><li><p>The done signal (what command passes when it&#8217;s complete?)</p></li></ol><p>No new tools. No worktrees yet. Write it down first.</p><p>Once that&#8217;s a habit, add Stage 1. The spec forces you to resolve assumptions before the agent makes them for you.<a href="https://harper.blog/2025/02/16/my-llm-codegen-workflow-atm/"> </a>This + a 15-minute planning session will save you 2+ hours of correction.</p><p>For parallel execution: git worktree add before you open a second tab.<br>One command, isolated branch, no collisions.</p><div class="callout-block" data-callout="true"><p><strong><a href="https://go.hungryminds.dev/agentic-loop-starter-kit">I put together a starter kit with every template from this post</a></strong>: the spec, the task planner prompt, the agent scoping format, the worktree setup script, and a lean CLAUDE.md ready to drop into any repo.</p></div><div><hr></div><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p>&#129504; <strong><a href="https://poolside.ai/models">A new contender just entered the coding model race</a></strong>: Jason Warner, GitHub&#8217;s former CTO&#8217;s startup, Poolside has released a new set of agentic coding models built specifically for software engineering workflows. This is a focused bet on where development is heading, with models designed for control, performance, and real-world coding tasks rather than general-purpose use.</p></li><li><p>&#9881;&#65039; <strong><a href="https://cursor.com/blog/typescript-sdk">Cursor just turned its agents into an API</a></strong>: Cursor&#8217;s new TypeScript SDK lets you invoke its coding agents from CI pipelines, backend services, or even your own product. This moves agents out of the editor and into the system itself, where they can run tasks, automate workflows, and become part of how software gets built.</p></li><li><p>&#9883;&#65039; <strong><a href="https://saschb2b.com/blog/react-compiler-year-in-review">React is still trying to automate performance</a></strong>: A year into the React Compiler experiment, the direction is becoming clearer. The goal is to shift performance optimization away from developers by letting the compiler handle memoization and rendering decisions, reducing the need for manual tuning in complex applications.</p></li><li><p>&#128994; <strong><a href="https://github.com/nodejs/node/pull/62526">Node 26 hit a delay, but Temporal is still close</a></strong>: Node 26.0 was expected to land with the Temporal API enabled by default, but a <a href="https://nodeweekly.com/link/184535/web">macOS-related problem</a> led to a last-minute delay. A <a href="https://nodeweekly.com/link/184536/web">fix is already in progress</a> and <a href="https://nodeweekly.com/link/184537/web">a new release candidate is available</a>, keeping Temporal on track to become part of the standard Node experience.</p></li><li><p>&#129504; <strong><a href="https://openai.com/index/where-the-goblins-came-from">Where the goblins in ChatGPT actually came from</a></strong>: OpenAI dug into a strange behavior where its models kept referencing goblins and other creatures in unrelated contexts. The cause wasn&#8217;t a bug in the usual sense. It came from subtle training incentives, especially around personality tuning, where certain kinds of metaphors were rewarded more than expected and then spread across the model. It&#8217;s a small, almost funny example, but it points to something deeper. Model behavior is shaped by many tiny signals, and those signals don&#8217;t always stay contained.</p><div><hr></div></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p>&#129513; <strong><a href="https://openai.com/index/open-source-codex-orchestration-symphony/">Coding is shifting from prompts to orchestration</a></strong>: This piece makes it clear that the real shift is not better prompts, but better coordination. Codex is evolving into a system that orchestrates multiple steps, tools, and agents to complete work. The focus is no longer on generating code, but on managing how that code gets produced.</p></li><li><p>&#9888;&#65039; <strong><a href="https://x.com/lifeof_jer/status/2048103471019434248">When output looks right but meaning drifts</a></strong>: A small example shows how generated output can subtly drift from the original intent while still looking correct. Nothing is obviously broken, which is exactly why it gets through. This is the kind of failure that does not show up in syntax or tests, but in meaning.</p></li><li><p>&#129504; <strong><a href="https://blog.happyfellow.dev/simulacrum-of-knowledge-work/">Work that looks complete but lacks depth</a>: </strong>This piece explores how modern tools can produce outputs that resemble real knowledge work without the depth behind them. The results look finished, but the reasoning is often shallow. That gap becomes clear the moment judgment is required.</p></li><li><p>&#128272; <strong><a href="https://muffin.ink/blog/scratch-svg-sanitization/">SVGs are still an easy way to get security wrong</a></strong>: SVGs look harmless, but they can carry scripts and unexpected behavior if not handled carefully. This write-up shows how common sanitization approaches fail and why this issue keeps slipping into production systems.</p></li><li><p>&#9883;&#65039; <strong><a href="https://certificates.dev/blog/accessibility-in-react-common-mistakes-and-how-to-fix-them">Most React accessibility issues are small and avoidable</a>: </strong>Accessibility problems in React apps rarely come from complex logic. They come from small gaps like missing labels, broken semantics, and poor keyboard support. These are easy to miss and just as easy to fix once you know where to look.</p><div><hr></div></li></ul><h1 style="text-align: center;"><strong>Tool of the Week</strong></h1><p>&#127916; <strong><a href="https://www.joshwcomeau.com/animation/scroll-driven-animations">Scroll-driven animations that finally feel natural</a></strong></p><p>Most scroll animations feel disconnected because they rely on timelines instead of user input. This guide shows how to tie motion directly to scroll position, which makes interactions smoother and easier to reason about. It&#8217;s a small shift in approach that fixes a surprisingly common UI problem.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item><item><title><![CDATA[WebDevPro #137: Why Blocking Code Breaks Node.js Performance ]]></title><description><![CDATA[Crafting the Web: Tips, Tools, and Trends for Developers]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-137-why-blocking-code-breaks</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-137-why-blocking-code-breaks</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Mon, 27 Apr 2026 14:01:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-Epb!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6c609e43-4006-496a-9adf-84620ff6b584_500x500.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 style="text-align: center;"><strong><a href="https://www.vpdae.com/redirect/7au88233yznh73di5miqvjri992">Catch the latest HubSpot Developer Platform updates in Spring Spotlight</a></strong></h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CI9M!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CI9M!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg" width="200" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Spotlight developer platform&quot;,&quot;title&quot;:&quot;Spotlight developer platform&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Spotlight developer platform" title="Spotlight developer platform" srcset="/__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 424w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 848w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 1272w, /__u/substackcdn.com/image/fetch/$s_!CI9M!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e79b42f-482a-4bda-aded-df60011bc089_200x300.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Spring Spotlight 2026 is live, and we&#8217;ve rounded up the top updates for developers. See what&#8217;s new for the HubSpot Developer Platform! Ship faster with AI coding tools like Cursor, Claude Code, and Codex.</p><p>Build MCP-powered AI connectors, run serverless functions with support for UI extensions, and use date-based versioning to streamline roadmap planning.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://developers.hubspot.com/spotlight?utm_campaign=41988443-DevRel%20%7C%20Spotlight%20%7C%20Spring%202026&amp;utm_source=email&amp;utm_medium=email&amp;utm_content=paved-newsletters&quot;,&quot;text&quot;:&quot;Explore Now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://developers.hubspot.com/spotlight?utm_campaign=41988443-DevRel%20%7C%20Spotlight%20%7C%20Spring%202026&amp;utm_source=email&amp;utm_medium=email&amp;utm_content=paved-newsletters"><span>Explore Now</span></a></p><div><hr></div><p>Welcome to this week&#8217;s issue of WebDevPro!</p><p>Node.js is often chosen for its ability to handle concurrent workloads efficiently. Its event-driven, non-blocking architecture allows applications to process multiple operations without waiting for each one to complete. This model is particularly effective in I/O-heavy systems where responsiveness matters more than sequential execution.</p><p>Because of this, there is a widespread assumption that Node.js applications will perform well under load by default. In practice, that assumption depends on one critical condition: the application must remain non-blocking. Once blocking behavior is introduced, the system begins to behave very differently.</p><p>Blocking code does not always cause immediate failures. In development environments, where concurrency is limited, the system may appear stable. Requests complete, responses are returned, and nothing seems obviously wrong. This creates a false sense of confidence in how the application will behave in production.</p><p>Under real-world conditions, however, multiple operations occur simultaneously, and the event loop becomes a shared dependency across all of them. At that point, the cost of blocking becomes visible. Delays accumulate, responsiveness drops, and the system begins to struggle under load. The issue is not simply that blocking code exists, but how it interacts with the event loop and how that interaction scales.</p><p>Before we get into it, here&#8217;s this week at a glance:</p><ul><li><p><em><strong>&#9883;&#65039; <a href="https://tanstack.com/blog/react-server-components">TanStack Start adds experimental React Server Components support</a></strong></em></p></li><li><p><em><strong>&#129302; <a href="https://code.claude.com/docs/en/routines">Claude introduces &#8220;routines&#8221; for repeatable workflows</a></strong></em></p></li><li><p><em><strong>&#129513; <a href="https://github.github.com/gh-stack/">GitHub introduces stacked PRs in private preview</a></strong></em></p></li><li><p><em><strong>&#127912; <a href="https://nerdy.dev/why-ai-sucks-at-front-end">Why AI still struggles with frontend development</a></strong></em></p></li><li><p><em><strong>&#129529; <a href="https://neciudan.dev/master-eslint-rules">Mastering ESLint rules for better code quality</a></strong></em></p></li><li><p><em><strong>&#127908; <a href="https://www.igalia.com/chats/npmxyz">Inside npmx, a new way to explore the npm ecosystem</a></strong></em></p></li></ul><h3><strong>What blocking means in practice</strong></h3><p>In Node.js, all JavaScript execution happens on a single thread, coordinated by the event loop. The event loop continuously processes tasks by retrieving them from a queue, executing them, and delegating work when possible so that execution can continue without interruption. This model works efficiently because it assumes that tasks will be short-lived and non-blocking.</p><p>Blocking code violates this assumption. When a blocking operation is executed, the event loop cannot proceed to the next task until the current one completes. This does not simply delay a single operation. It prevents all other pending tasks from executing during that time, effectively pausing the system&#8217;s ability to make progress.</p><p>The impact is broader than it first appears. Callbacks that are ready to run remain in the queue. Timers do not fire at expected intervals. Incoming requests must wait before they can even begin execution. What appears to be a local delay becomes a system-wide bottleneck.</p><p>This shared delay is what makes blocking code particularly problematic in Node.js. In systems with multiple threads, delays can be absorbed or distributed. In Node.js, the delay is centralized. A single blocking operation affects everything else that depends on the event loop.</p><h3><strong>Why synchronous APIs are risky in Node.js</strong></h3><p>Synchronous APIs are often attractive because they simplify reasoning about code. Execution flows in a straight line, making it easier to follow and debug. This simplicity can be beneficial in scripts or isolated tasks where concurrency is not a concern.</p><p>In an event-driven system like Node.js, however, synchronous APIs introduce a significant limitation. Because they execute on the main thread, they block the event loop until they complete. During this time, no other operations can be processed, regardless of how unrelated they may be.</p><p>This creates a disconnect between how the code appears and how it behaves under load. The code may look efficient and predictable, but its execution forces all operations into a sequential pattern. Instead of handling multiple tasks concurrently, the system processes them one at a time.</p><p>The result is reduced flexibility in how work is handled. As more operations depend on the event loop, the impact of synchronous APIs becomes more pronounced. What begins as a simple design choice can evolve into a system-wide constraint.</p><h3><strong>CPU-intensive work and hidden blocking</strong></h3><p>Blocking behavior is not limited to I/O operations. CPU-intensive tasks can have an even greater impact, particularly when executed synchronously. These operations consume the main thread for their duration, preventing the event loop from processing other tasks.</p><p>The book illustrates this with a cryptographic key generation example, where a synchronous function is executed repeatedly. Each iteration runs on the main thread, and the cumulative effect is a prolonged period during which the event loop is unavailable.</p><p><code>performance.mark(&#8221;start-sync&#8221;); <br>for (let i = 0; i &lt; 10000; i++) { <br>generateKeyPairSync(&#8221;rsa&#8221;, { <br>modulusLength: 1024, <br>}); <br>} <br>performance.mark(&#8221;end-sync&#8221;); <br>performance.measure(&#8221;generateKeyPairSync&#8221;, &#8220;start-sync&#8221;, &#8220;end-sync&#8221;);</code></p><p>In this scenario, the cost is not just the execution time of a single operation, but the accumulation of blocking across many iterations. The event loop remains occupied for the entire duration, preventing any other work from progressing.</p><p><code>performance.mark(&#8221;start-async&#8221;); <br>for (let i = 0; i &lt; 10000; i++) { <br>generateKeyPair(&#8221;rsa&#8221;, { modulusLength: 1024 }, <br>() =&gt; {}); <br>} <br>performance.mark(&#8221;end-async&#8221;); <br>performance.measure(&#8221;generateKeyPair&#8221;, &#8220;start-async&#8221;, &#8220;end-async&#8221;);</code></p><p>The asynchronous version changes how the work is executed. Instead of occupying the main thread, the operations are delegated, allowing the event loop to remain available. This difference has a direct impact on system responsiveness, especially under load.</p><h3><strong>The performance gap is not theoretical</strong></h3><p>The difference between synchronous and asynchronous execution is not just conceptual. The book demonstrates that the impact can be measured and observed in practice. The synchronous version of the operation takes significantly longer and blocks execution entirely.</p><p>The asynchronous version, by contrast, allows the system to continue processing other tasks while the work is being performed. This leads to better utilization of system resources and improved responsiveness.</p><p>This distinction changes how performance should be evaluated. In Node.js, performance is not only about how quickly a single operation completes. It is about whether the system can continue to process other work during that time.</p><p>A fast operation that blocks the event loop can still degrade system performance if it prevents other tasks from progressing. Conversely, a slower operation that does not block may have less overall impact on system responsiveness.</p><h3><strong>Why this matters more under load</strong></h3><p>Blocking behavior becomes more problematic as concurrency increases. In low-load environments, tasks are processed with minimal overlap, and delays may not be noticeable. The system appears stable because there is little competition for the event loop.</p><p>As the number of concurrent operations grows, the situation changes. Multiple tasks begin to depend on the event loop at the same time. Each task expects to be processed in a timely manner, and blocking operations disrupt this expectation.</p><p>When a blocking operation runs, it prevents the event loop from servicing other tasks. These tasks begin to accumulate in the queue, increasing wait times and reducing overall throughput. The system becomes less responsive as more work is added.</p><p>This is why blocking issues often appear only under load. The system needs sufficient concurrency for the delays to become visible. Once that threshold is reached, the impact becomes more pronounced, and performance begins to degrade more rapidly.</p><h3><strong>Async wrappers do not remove blocking</strong></h3><p>A common misconception is that using asynchronous syntax automatically makes an operation non-blocking. Wrapping a function in async/await or a callback does not change how the underlying work is executed.</p><p>The book emphasizes that async syntax controls how results are handled, not how the work itself is performed. If the underlying operation is synchronous and CPU-intensive, it will still block the event loop.</p><p>This distinction highlights the difference between code structure and execution behavior. A function may appear asynchronous in form while still behaving in a blocking manner in practice.</p><p>Understanding this difference is essential for identifying performance issues. Changing syntax without addressing the underlying execution does not resolve the problem.</p><h3><strong>Reducing work instead of changing execution</strong></h3><p>In some cases, improving performance is not about changing how an operation executes, but reducing how often it runs. The book demonstrates this with an example where repeated computation is replaced with a caching approach.</p><p>Instead of recalculating an expensive result for every request, the system stores the result and updates it periodically. This reduces the number of times the operation is executed, lowering the load on the event loop.</p><p>l<code>et cachedSignature = null; <br>const app = Express();</code></p><p><code>const signingMiddleware = (_req, res, next) =&gt; { <br>if (!cachedSignature) { <br>console.info(&#8221;Signature is not cached&#8221;); <br>cachedSignature = signData(Date.now().toString()); <br>setInterval( <br>() =&gt; (cachedSignature = signData(Date.now().toString())), <br>10000); <br>} <br><br>res.setHeader( <br>&#8220;X-Signature&#8221;, `data=${cachedSignature.data.toString()};kid=${cachedSignature.keyId};sha512=${cachedSignature.signature}`); <br>next(); <br>};</code></p><p>This approach shifts the focus from execution style to execution frequency. By reducing repeated work, the system becomes more efficient without changing how the operation itself is implemented.</p><h3><strong>The real trade-off</strong></h3><p>Blocking code is often easier to write and reason about. Its linear execution model provides clarity and predictability, making it attractive in many situations.</p><p>However, this simplicity comes at a cost in systems that rely on concurrency. Blocking operations limit the ability of the event loop to process multiple tasks efficiently, reducing overall system responsiveness.</p><p>The trade-off is not simply between synchronous and asynchronous code. It is between local simplicity and system-wide performance. A piece of code that is easy to understand in isolation may introduce constraints that affect the entire application.</p><p>As systems scale and concurrency increases, this trade-off becomes more significant. Decisions that seem minor at the code level can have substantial impact at the system level.</p><h3><strong>Key Takeaways</strong></h3><ul><li><p>Blocking code prevents the event loop from processing other tasks, creating system-wide delays.</p></li><li><p>Synchronous APIs introduce constraints that limit concurrency.</p></li><li><p>CPU-intensive operations can block execution even when used with async syntax.</p></li><li><p>Performance in Node.js depends on maintaining event loop availability.</p></li><li><p>Reducing execution frequency can improve efficiency without changing execution style.</p></li></ul><h3><strong>Final Thoughts</strong></h3><p>Node.js relies on a responsive event loop to handle concurrent operations effectively. Blocking code disrupts this model by introducing delays that affect all tasks, not just the one being executed.</p><p>The impact of blocking behavior is not always immediate, but it becomes clear under load. As concurrency increases, the system&#8217;s ability to process work efficiently depends on keeping the event loop free.</p><p>If you&#8217;d like to read more on this, <em><strong><a href="https://www.amazon.com/Node-js-Design-Patterns-production-grade-applications/dp/1803238941/ref=sr_1_2">Node.js Design Patterns</a></strong></em> explores these ideas in depth, especially around asynchronous behavior, performance, and the architectural decisions behind scalable Node.js systems.</p><h1 style="text-align: center;"><strong>This Week in the News</strong></h1><ul><li><p>&#129504; <strong><a href="https://blog.pragmaticengineer.com/the-pulse-tokenmaxxing-as-a-weird-new-trend/?utm_source=chatgpt.com">Tokenmaxxing is the new productivity metric developers are gaming</a></strong>: There&#8217;s a new habit showing up in AI-heavy workflows. Teams are starting to track and even optimize for token usage, treating it as a signal of productivity. Gergely Orosz digs into why that framing breaks down quickly. More tokens don&#8217;t mean better outcomes, just more input. It&#8217;s the same pattern developers have seen before with lines of code and story points, now resurfacing in an AI-shaped form. What&#8217;s interesting here is not the trend itself, but how quickly it appeared. Even with new tools, the instinct to measure the wrong thing hasn&#8217;t changed.</p></li><li><p>&#9889; <strong><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-7-0-beta/">TypeScript 7.0 goes 10x faster with a Go rewrite</a></strong>: The TypeScript team has released the 7.0 beta after spending the past year porting the entire compiler to Go. The result is a version that&#8217;s roughly 10x faster than TypeScript 6.0, while keeping the type-checking behavior structurally the same. For developers, that means no major migration or new errors to worry about. Just significantly faster builds out of the box.</p></li><li><p>&#128994; <strong><a href="https://github.com/nodejs/node/pull/61806">Node.js moves toward Temporal API and stabilizes key features</a>: </strong>Node.js is preparing to support the Temporal API by default, likely landing in the upcoming v26 release. This brings a modern, more reliable alternative to JavaScript&#8217;s existing Date handling into the runtime. At the same time, Node.js 24.15.0 (LTS) marks require(esm), and the module compile cache as stable, and introduces a new --max-heap-size flag. Together, these updates signal continued progress in both runtime capabilities and performance tooling.</p></li><li><p><strong>&#128231; <a href="https://resend.com/blog/react-email-6">React Email 6 simplifies a fragmented ecosystem</a></strong>:<strong> </strong>React Email 6 introduces a major update focused on cleaning up versioning issues across its ecosystem. The release makes it easier to manage dependencies and ensures the CLI and components stay in sync. For teams building email templates with React, this should reduce friction and make the overall workflow more predictable.</p></li></ul><h1 style="text-align: center;"><strong>Beyond the Headlines</strong></h1><ul><li><p>&#9889; <strong><a href="https://voidzero.dev/posts/oxc-angular-compiler">A new Angular compiler built on Oxc</a></strong>: This post explores an experimental Angular compiler powered by <strong>Oxc</strong>, a Rust-based toolchain focused on performance. The goal is to significantly speed up builds and modernize the compilation pipeline. It&#8217;s another signal that JavaScript tooling is steadily moving toward Rust-based infrastructure to push performance boundaries.</p></li><li><p>&#128680; <strong><a href="https://awesomeagents.ai/news/github-fake-stars-investigation/">Investigating fake stars in GitHub repos</a></strong>: This investigation looks into how some GitHub repositories inflate their popularity using fake stars. It highlights how easily perception can be manipulated and why star counts aren&#8217;t always a reliable signal of quality. For developers, it&#8217;s a reminder to evaluate projects based on code, activity, and community, not just metrics.</p></li><li><p><strong>&#127909; <a href="https://www.youtube.com/watch?v=a9_Ud5MFTjU">Rethinking modern web architecture</a></strong>: This talk dives into how modern web architecture is evolving, covering trade-offs in performance, complexity, and developer experience. It offers a broader perspective on how current patterns scale in real-world applications. A useful watch if you&#8217;re thinking beyond frameworks and into long-term system design.</p></li><li><p>&#129513; <strong><a href="https://nesbitt.io/2026/04/16/features-everyone-should-steal-from-npmx.html">Features worth borrowing from npmx</a>: </strong>This article breaks down specific ideas from the npmx project that could inspire better developer tools. It highlights practical features that improve how developers explore and interact with packages. The takeaway is simple. Good tooling often comes from rethinking small UX details.</p></li><li><p><strong>&#128736;&#65039; <a href="https://cursor.com/blog/app-stability">Why app stability matters more than ever</a>: </strong>Cursor shares insights into building stable applications, especially in environments where rapid iteration and AI-assisted development are becoming the norm. The focus is on reducing breakage and maintaining reliability as systems evolve. It&#8217;s a reminder that speed is valuable, but stability is what keeps users and teams productive.</p></li></ul><h1 style="text-align: center;"><strong>Tool of the Week</strong></h1><p>&#127916; <strong><a href="https://animata.design/">Add unique animations to your React apps</a></strong></p><p>Building engaging UIs often means going beyond basic transitions. <strong><a href="https://animata.design/">Animata</a></strong> offers a collection of 100+ animation-focused React components, including effects like animated beams, spreading cards, and even a Slack-style intro screen.</p><p>It&#8217;s a handy resource if you want to add more personality to your UI without building complex animations from scratch.</p><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item><item><title><![CDATA[WebDevPro #136: Agent Continuous Learning Framework: Build Traces, Refine Context, Iterate with a Harness (like DeerFlow), then Fine‑Tune the Model]]></title><description><![CDATA[By Daniel He, Co&#8209;Author of DeerFlow]]></description><link>https://packtwebdevpro.substack.com/p/webdevpro-136-agent-continuous-learning</link><guid isPermaLink="false">https://packtwebdevpro.substack.com/p/webdevpro-136-agent-continuous-learning</guid><dc:creator><![CDATA[Kinnari Chohan]]></dc:creator><pubDate>Wed, 22 Apr 2026 14:02:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!PZU4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to this week&#8217;s bonus issue of WebDevPro!</p><p>DeerFlow is an open&#8209;source SuperAgent framework based on LangGraph, focusing on multi&#8209;agent orchestration, with 60K+ stars on GitHub. Core contributor Daniel He has long been deeply involved in agent workflow design and stateful graph execution, and he is dedicated to pushing autonomous agents to the true limits of production environments.</p><p><em>This article is a speaker feature for an upcoming <strong>FREE</strong> live session hosted by Packt, where the DeerFlow team will walk through how these ideas translate into real systems.</em></p><h1 style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/build-ai-agents-with-deerflow-20-tickets-1987511310833?aff=webdevpro">Build AI Agents with DeerFlow 2.0</a></strong></h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!PZU4!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 424w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 848w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!PZU4!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/92599987-270c-4650-a493-6436d4be045b_1880x940.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Deerflow 2.0 FREE workshop &quot;,&quot;title&quot;:&quot;Deerflow 2.0 FREE workshop &quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Deerflow 2.0 FREE workshop " title="Deerflow 2.0 FREE workshop " srcset="/__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 424w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 848w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!PZU4!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F92599987-270c-4650-a493-6436d4be045b_1880x940.webp 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p style="text-align: center;">Expect live demos, including agents that review pull requests and generate full research reports from a single prompt, along with a closer look at how DeerFlow coordinates models like GPT, Gemini, and DeepSeek behind the scenes.</p><p style="text-align: center;">The event is designed for engineers, AI practitioners, product teams, and anyone exploring autonomous workflows or open source agent systems. It also includes insights from the maintainers on how the project evolved from DeerFlow 1.0 to 2.0, what is coming next, and how to get involved.</p><p style="text-align: center;"><strong>&#128197;</strong> <strong><a href="https://www.eventbrite.co.uk/e/build-ai-agents-with-deerflow-20-tickets-1987511310833?aff=webdevpro">Wednesday, May 6 &#8226; 9 AM &#8211; 10:30 AM EDT</a></strong><br><strong>&#127760; <a href="https://www.eventbrite.co.uk/e/build-ai-agents-with-deerflow-20-tickets-1987511310833?aff=webdevpro">Free online event by Packt Publishing</a></strong></p><p style="text-align: center;"><strong><a href="https://www.eventbrite.co.uk/e/build-ai-agents-with-deerflow-20-tickets-1987511310833?aff=webdevpro">Register now</a></strong></p><div><hr></div><p>How exactly can an agent system &#8220;continuously become stronger&#8221;?</p><p>It relies on training data, training infrastructure, and evaluation loops, which makes this kind of continuous improvement a platform-level capability rather than something a typical product team can iterate on frequently.</p><p>Recently, Harrison Chase, the founder of LangChain, <a href="https://x.com/hwchase17/status/2040467997022884194?s=20">posted an X thread</a> that breaks down the Agent Continuous Learning system into three layers: <strong>Model</strong> (model weights), <strong>Harness</strong> (execution mechanism), and <strong>Context</strong> (configurable memory). He then combined this with cutting-edge work such as <strong><a href="https://yoonholee.com/meta-harness/">Meta-Harness</a></strong> and <strong><a href="https://github.com/langchain-ai/deepagents">LangChain Deep Agents</a></strong> to analyze the learning methods, implementation costs, and applicable scenarios of each layer.</p><p>Based on this analysis, a possible action path for product teams is: first, get Traces right, then do Context learning, then establish a Harness optimization loop, and finally consider model fine-tuning.</p><p>The core argument of this article is very clear:</p><p><em>For AI agents, &#8220;continuous learning&#8221; should not be understood as merely updating model weights. An agent system can actually evolve continuously on three levels: Model, Harness, and Context.</em></p><h1><strong>Core Framework: Three-Layer Agent Learning</strong></h1><p>In his thread, Harrison broke down the agentic system into three layers:</p><ul><li><p><strong>Model:</strong> The underlying model itself, which is the weights.</p></li><li><p><strong>Harness:</strong> The &#8220;shell&#8221; that drives the model&#8217;s operation, including agent code, fixed tools, fixed hints, execution loops, etc.</p></li><li><p><strong>Context:</strong> A configurable context located outside of the harness, such as memory files, skills, user configurations, and team configurations.</p></li></ul><p>The value of this definition lies in the fact that it expands &#8220;learning&#8221; from a single model training problem into a complete systems engineering problem.</p><h1><strong>Layer-by-layer interpretation</strong></h1><p><strong>1. Model layer: The most traditional, but also the most complex layer.</strong></p><p>This layer corresponds to continuous learning, which is most familiar to everyone:</p><ul><li><p>Update weights using methods such as SFT and RL.</p></li><li><p>It is also possible to use a more fine-grained adaptation method, such as LoRA.</p></li><li><p>The goal is to make the model perform better on new tasks.</p></li></ul><p>However, an old problem persists: <strong>catastrophic forgetting</strong>. That is, after the model learns new things, its old abilities actually degenerate.</p><p>Daniel&#8217;s judgment is:</p><ul><li><p>For most teams, the Model layer has the highest continuous learning cost.</p></li><li><p>It relies on training data, training infrastructure, evaluation loop, and model deployment mechanism.</p></li><li><p>This is more like a platform-level capability than a routine method that a typical product team can frequently iterate upon.</p></li></ul><p>So while Harrison acknowledges the importance of the model layer, his real focus is not there.</p><p><strong>2. Harness Layer: The most underestimated leverage point in agent engineering over the past year.</strong></p><p>&#8220;Harness&#8221; does not refer to the model itself, but rather &#8220;how the model is used&#8221;:</p><ul><li><p>How to write system prompts</p></li><li><p>How to expose tools to the model</p></li><li><p>How to organize loop calls</p></li><li><p>When to truncate the context, when to retry, and when to determine if the task is complete</p></li><li><p>Which logs and traces are saved for later analysis</p></li></ul><p>Harrison specifically mentions the work at https://yoonholee.com/meta-harness/ in his article. Its main idea can be summarized as follows:</p><ol><li><p>Run the agent on a batch of tasks.</p></li><li><p>Collect complete execution logs and scores.</p></li><li><p>Store these historical candidates, source code, and execution traces in the filesystem.</p></li><li><p>Then, have a coding agent read these materials and propose new harness modifications.</p></li><li><p>Evaluating the new harness and continuing its iterations.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!AfbA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 424w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 848w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!AfbA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp" width="1080" height="385" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:385,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 424w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 848w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!AfbA!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc6ea11c4-0cf2-4541-9c61-4302d608eaf0_1080x385.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This is important because it illustrates:</p><ul><li><p>Agent evolution can be achieved without changing the model, only the runtime framework.</p></li><li><p>The truly high-value optimization targets are often the &#8220;execution mechanism&#8221; rather than the &#8220;parameters&#8221;.</p></li><li><p>The more complete the traces, the more harness optimization resembles an engineering iteration rather than a matter of guesswork and prompt adjustments.</p></li></ul><p>The official <a href="https://yoonholee.com/meta-harness/">Meta-Harness</a> page also presents strong results: it emphasizes its key difference by allowing the optimizer to see the complete historical code, scores, and execution traces, rather than just a summary. The authors claim that this &#8220;filesystem-level context&#8221; can increase the available diagnostic information for each round of optimization by an order of magnitude compared to traditional methods.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!CeI6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 424w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 848w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_webp, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!CeI6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp" width="1080" height="583" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:583,&quot;width&quot;:1080,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="/__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_424, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 424w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_848, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 848w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_1272, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 1272w, /__u/substackcdn.com/image/fetch/$s_!CeI6!, /__u/packtwebdevpro.substack.com/w_1456, /__u/packtwebdevpro.substack.com/c_limit, /__u/packtwebdevpro.substack.com/f_auto, /__u/packtwebdevpro.substack.com/q_auto:good, /__u/packtwebdevpro.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9d2fd5b9-63fd-4663-a144-598d992ab2f3_1080x583.webp 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>3. Context Layer: Closest to the business logic, and best suited for initial implementation</strong></p><p>Harrison defines Context as content located <em>outside</em> of the harness used to configure the agent, for example:</p><ul><li><p>Instructions</p></li><li><p>Skills</p></li><li><p>Tools</p></li><li><p>Memory files</p></li><li><p>User preferences</p></li><li><p>Team rules</p></li></ul><p>The key to this layer is not &#8220;what the model has learned&#8221;, but &#8220;what the system has remembered and how to continue using it in subsequent sessions&#8221;.</p><p>The official documentation for LangChain Deep Agents explains this in great detail. It supports:</p><ul><li><p><strong>Agent-scoped memory</strong>: All users share the same agent memory.</p></li><li><p><strong>User-scoped memory</strong>: Each user has their own independent memory.</p></li><li><p><strong>Online update</strong>: Write directly to memory during session</p></li><li><p><strong>Backend organization</strong>: Performing consolidation outside the session</p></li><li><p><strong>Skills</strong> are a form of procedural memory, loaded only when needed.</p></li></ul><p>This shows that the Context layer is not as simple as &#8220;adding more prompts&#8221;, but a persistent, hierarchical, readable, writable, and searchable memory system.</p><h1><strong>Two examples from the author</strong></h1><p>Harrison used two mappings in the article, first for Claude Code:</p><ul><li><p><strong>Models:</strong> Claude Sonnet, and so on</p></li><li><p><strong>Harness:</strong> Claude Code itself</p></li><li><p><strong>User context:</strong> CLAUDE.md, /skills, mcp.json</p></li></ul><p>This breakdown is very practical because it directly illustrates:</p><ul><li><p>Your perception that &#8220;Claude Code has become smarter&#8221; may not necessarily be due to changes in the model weights;</p></li><li><p>It&#8217;s also possible that the harness has been changed;</p></li><li><p>Or perhaps you&#8217;ve configured its context better;</p></li></ul><p>And again for OpenClaw:</p><ul><li><p><strong>Model:</strong> Can accept multiple models</p></li><li><p><strong>Harness:</strong> Pi (powers OpenClaw) plus some running scaffolding</p></li><li><p><strong>Agent context</strong>: SOUL.md and skills from ClawHub</p></li></ul><p>OpenClaw&#8217;s public documentation says:</p><ul><li><p>SOUL.md is officially defined as the personality and tone configuration file for the agent</p></li><li><p>ClawHub is defined as a public registry of OpenClaw skills/plugins</p></li></ul><p>This precisely confirms Harrison&#8217;s point: the &#8220;continuous learning&#8221; of many agent systems essentially occurs at the configurable context layer, rather than the model fine-tuning layer.</p><h1><strong>The paradigm shift this article aims to promote</strong></h1><p>To summarize this article into a single sentence:</p><p>An agent&#8217;s continuous learning is shifting from &#8220;training the model&#8221; to &#8220;optimizing the entire system&#8221;.</p><p>There are at least three changes here:</p><p><strong>1. The learning objective shifts from weights to system behavior.</strong></p><p>Traditional LLM continuous learning focuses more on:</p><ul><li><p>Has the loss decreased?</p></li><li><p>Has the benchmark improved?</p></li></ul><p>Agent continuous learning focuses more on:</p><ul><li><p>Are they better at using tools?</p></li><li><p>Are they better at planning steps?</p></li><li><p>Can we improve our execution strategies by learning from past failures?</p></li><li><p>Do we have a better understanding of the preferences of current users, teams, or organizations?</p></li></ul><p><strong>2. The learning unit is shifting from a single model to a hierarchical architecture.</strong></p><p>Within the same agent system, the learning frequency and cost of different layers are completely different:</p><ul><li><p>Model: Low frequency, high cost, platform level</p></li><li><p>Harness: Mid-frequency, engineering-driven, measurable</p></li><li><p>Context: High frequency, business-driven, most likely to occur online</p></li></ul><p>This means that continuous learning should not have just one master switch, but rather three different mechanisms.</p><p><strong>3. Traces become a unified fuel.</strong></p><p>Harrison repeatedly emphasizes &#8220;traces&#8221; at the end of the article. This is one of the most crucial infrastructure assessments in the entire text.</p><p>The reason is straightforward:</p><ul><li><p>To modify the model, you need traces as a source of training/preference data</p></li><li><p>To improve harnessing, you need traces as diagnostic material for failures</p></li><li><p>To modify the context, you need traces as source material for experience extraction</p></li></ul><p>In other words, without high-quality traces, there is no high-quality agent learning loop.</p><h1><strong>Some personal judgments</strong></h1><p><strong>1. The Context layer will be the first to become widespread.</strong></p><p>Daniel believes that the Context layer, rather than the Model layer, will be the first of the three layers to be widely implemented.</p><p>Reasons:</p><ul><li><p>No training required</p></li><li><p>The most direct impact on business returns</p></li><li><p>Isolation can be done by user / team / org</p></li><li><p>Easy to manage permissions and rollback</p></li><li><p>Easier to meet the controllability requirements of enterprise systems</p></li></ul><p>Many capabilities that are packaged today as &#8220;the agent can remember&#8221; essentially belong to this level.</p><p><strong>2. The Harness layer will become the focus of the next round of agent infrastructure competition.</strong></p><p>If 2024 was mainly about who could get their agent up and running first, then 2026 will be more about:</p><ul><li><p>Whose harness is more stable?</p></li><li><p>Whose traces are more complete?</p></li><li><p>Whose evaluation and replay system is more closed-loop?</p></li><li><p>Who can quickly incorporate failure cases into the next version of agent behavior improvement?</p></li></ul><p>This is why work like Meta-Harness is worth paying attention to. It represents a very engineering-oriented approach: letting the agent help you change the agent.</p><h1><strong>Practical reflections on your team&#8217;s development of agent products</strong></h1><p>If your team is planning to incorporate &#8220;continuous learning&#8221; into its agent roadmap, Daniel recommend proceeding in the following order:</p><p><strong>Phase 1: First, get the traces right</strong></p><ul><li><p>Unified recording of task input, tool calls, key intermediate states, output results, and human feedback</p></li><li><p>Preserve reviewable evidence for failed tasks, rather than just reporting an error</p></li><li><p>Add user/org/task/version dimension tags to traces</p></li></ul><p><strong>Phase 2: Prioritize context learning</strong></p><ul><li><p>Start with user preferences, team rules, glossary, and standard operating procedures (SOPs)</p></li><li><p>Distinguish between read-only memory and writable memory</p></li><li><p>Define the scope: which is user-level and which is org-level</p></li><li><p>Support both online writing and offline defragmentation update paths</p></li></ul><p><strong>Phase 3: Establish the Harness optimization loop</strong></p><ul><li><p>Run the agent continuously on a standard task set</p></li><li><p>Perform A/B testing and automated evaluation on the harness version</p></li><li><p>The coding agent reads traces to help propose candidates for harness modification</p></li><li><p>Establish a rollback mechanism to prevent situations where changing the prompt might seem convenient in the short term, but ultimately leads to decreased overall stability</p></li></ul><p><strong>Phase 4: Consider model-level learning only as a last resort</strong></p><ul><li><p>Only when you have accumulated enough high-quality traces</p></li><li><p>Furthermore, optimizations at the harness/context layer are nearing their limits</p></li></ul><h1><strong>A reusable decision framework</strong></h1><p>When faced with the question &#8220;How should an agent undergo learning?&#8221;, you can first ask four questions:</p><ol><li><p>What needs to be changed this time? Is it the model capabilities, the operating mechanism, or the configurable memory?</p></li><li><p>Should this change apply to the agent, user, or org scope?</p></li><li><p>Should this update occur immediately during runtime, or should it be processed in an offline task before taking effect?</p></li><li><p>Are there enough complete traces to support an assessment of whether this learning was truly effective?</p></li></ol><p>If these four questions cannot be answered clearly, the so-called &#8220;continuous learning&#8221; is most likely just a vague slogan.</p><h1><strong>Conclusion</strong></h1><p>The value of Harrison Chase&#8217;s post and accompanying article lies not in proposing a completely new algorithm, but in breaking down agent continuous learning into a more practical three-layer framework:</p><ul><li><p>Model learning addresses underlying capabilities</p></li><li><p>Harness learning addresses the execution mechanism</p></li><li><p>Context learning addresses memory and personalization</p></li></ul><p>The two things that the product team should act on immediately are not training the model, but rather:</p><ul><li><p>Build the infrastructure for traces</p></li><li><p>Productize the context and harness learning loop</p></li></ul><p>This is also Daniel&#8217;s core conclusion for this article: stronger agents in the future will not necessarily come from larger models, but are more likely to come from systems that are better at &#8220;reviewing, remembering, and reconstructing&#8221;.</p><div><hr></div><p><em>Want to know more about how to turn your prompts into workflows with an agent harness? Join us for a live demonstration of <strong>DeerFlow 2.0</strong> on <strong>6th May</strong>! </em><br><em><strong><a href="https://www.eventbrite.co.uk/e/build-ai-agents-with-deerflow-20-tickets-1987511310833?aff=webdevpro">Register now!</a></strong></em></p><div><hr></div><p><em>That&#8217;s all for this week. Have any ideas you want to see in the next article? Hit Reply!</em></p><p>Cheers!</p><p>Editor-in-chief,</p><p>Kinnari Chohan</p><div><hr></div><p><strong>&#128075; <a href="https://landing.packtpub.com/sponsorship-landing-page/">Advertise with us</a></strong><br><strong> </strong>Interested in sponsoring this newsletter and reaching a highly engaged audience of tech professionals? Simply reply to this email, and our team will get in touch with the next steps.</p>]]></content:encoded></item></channel></rss>