<?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[Max Gale]]></title><description><![CDATA[Max Gale]]></description><link>https://blog.maxgale.co</link><image><url>https://substackcdn.com/image/fetch/$s_!AGn9!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F73f2f376-c596-4a39-8429-7537375e2ace_144x144.png</url><title>Max Gale</title><link>https://blog.maxgale.co</link></image><generator>Substack</generator><lastBuildDate>Sat, 10 Oct 2026 03:49:25 GMT</lastBuildDate><atom:link href="https://blog.maxgale.co/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Max Gale]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[maxgaledotco@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[maxgaledotco@substack.com]]></itunes:email><itunes:name><![CDATA[Max Gale]]></itunes:name></itunes:owner><itunes:author><![CDATA[Max Gale]]></itunes:author><googleplay:owner><![CDATA[maxgaledotco@substack.com]]></googleplay:owner><googleplay:email><![CDATA[maxgaledotco@substack.com]]></googleplay:email><googleplay:author><![CDATA[Max Gale]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Keeping “engineering” in “software engineering”]]></title><description><![CDATA[Most of the time, prompting coding agents is not practicing the art of software engineering.]]></description><link>https://blog.maxgale.co/p/keeping-engineering-in-software-engineering</link><guid isPermaLink="false">https://blog.maxgale.co/p/keeping-engineering-in-software-engineering</guid><dc:creator><![CDATA[Max Gale]]></dc:creator><pubDate>Sun, 30 Aug 2026 13:52:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!2l2s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most of the time, prompting coding agents is not practicing the art of software engineering. And that&#8217;s totally fine. Sometimes, you simply want to describe a feature in a few sentences and have the agent spit out the changes. But other times, you want real engineering, which involves applying well-reasoned consideration to the constraints, the current state of the code, and possible future states, and then combining these concepts into an elegant solution that minimally meets the requirements in a way that&#8217;s scalable and extensible.</p><p>We don&#8217;t engineer for engineering&#8217;s sake. We do it so future code changes can be made clearly and concisely, in a way that&#8217;s unlikely to introduce new bugs. And it&#8217;s this last part in particular, that bites unstructured agentic code bases in the proverbial ass. Over time, as the complexity of the application grows, numerous issues develop if care is not taken: inadvertent dependencies get added, invariants that guided the original design are forgotten, and the amount of bespoke state balloons. As a result, prompting the agent to change one piece of functionality frequently breaks another or leaves the application in an inconsistent state. </p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.maxgale.co/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! 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><p>Additionally, prompting is <em>imperative</em> not <em>declarative</em>. We never lay out exactly what we want, instead opting to guide agents in small increments over and over again. Often, in between these steps, the agent will have forgotten something we said earlier.</p><p>That&#8217;s not to say we should stop prompting agents. We should continue to leverage them to speed up development. But we need higher-level primitives that allow us to specify the important parts of a design and have the agent enforce those across multiple changes and iterations. </p><p>Over the last few weeks, I&#8217;ve been experimenting with ways of doing this, and I my current flow <em>kind of</em> works. It&#8217;s not perfect by any means, but overall trying to deal in higher level abstractions has been a breath of fresh air compared to dealing with prompt minutia.</p><p>The idea is to turn projects into long-lived specifications. These aren&#8217;t &#8220;plans&#8221; and they aren&#8217;t agent generated. The specifications are a human-digestible transcription of the core ideas and invariants of a design. If I want to update something about the engineering design, I simply update the spec to include the new information. Then, I ask my agent to look at what changed in the spec and to update the code so that the spec is accurate.</p><p>Here&#8217;s how it works:</p><blockquote><p>Note: I packaged up everything below into a <a href="https://github.com/dimes/specling">plugin</a>. Feel free to use it. How it works is very simple, so you can leverage the basic principals without the plugin.</p></blockquote><h3>Step 1 - Define a spec file (Markdown is fine)</h3><p>The spec file defines what you want to build. At first, it just contains your initial idea, but it will grow over time as your specify new functionality. This is the master plan of your application. <strong>Do not slop it up</strong>. This should be simple and human-digestible.</p><h3>Step 2 - Update the spec</h3><p>Define your new functionality, in a declarative way, leveraging composition with previous parts of the spec where applicable.</p><h3>Step 3 - Kick of your agent</h3><p>Rather than instruct your agent to implement the feature directly, you direct the agent to look at changes to your spec and figure out what should be done.</p><blockquote><p>Tip: You can commit the changes to your spec and kick of the agent in a worktree so that you can continue specing out other functionality. If you&#8217;re using the <a href="https://github.com/dimes/specling">plugin</a>, just run specling skill with your spec file to do this automatically. </p></blockquote><p>And that&#8217;s basically it.</p><h2>Example</h2><p>What does a TODO app look like with declarative prompting?</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;3aa134aa-3e44-4022-bdb3-600872ce366b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown"># TODO app

An web application that shows a list of TODOs. A plus button at the top of the list 
adds a new TODO.

## Data model

type Todo = string</code></pre></div><p>That leaves you with a standard looking TODO app</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2l2s!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2l2s!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 424w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 848w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 1272w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2l2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png" width="1400" height="696" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:696,&quot;width&quot;:1400,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:125768,&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://blog.maxgale.co/i/212601552?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2l2s!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 424w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 848w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.png 1272w, https://substackcdn.com/image/fetch/$s_!2l2s!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3b6d6d9f-e5d3-46ff-a726-27db1f07e58a_1400x696.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 buttonBase-GK1x3M"><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" class="icon-noB79L"><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 buttonBase-GK1x3M"><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 icon-noB79L"><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 looks ok, but we can&#8217;t mark them as done. Let&#8217;s update our data model:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;ef9c9ca8-06fc-41ba-b9c4-da3a66824643&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">&gt; git diff
diff --git a/todo.spec.md b/todo.spec.md
index c50d17c..2f89f96 100644
--- a/todo.spec.md
+++ b/todo.spec.md
@@ -5,4 +5,4 @@ adds a new TODO.
 
 ## Data model
 
-type Todo = string
+type Todo = { text: string; done: boolean }</code></pre></div><p>A simple change to our data model lets the agent know what type of functionality we want.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sWHs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sWHs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 424w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 848w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 1272w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sWHs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png" width="1354" height="630" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:630,&quot;width&quot;:1354,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:102419,&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://blog.maxgale.co/i/212601552?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sWHs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 424w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 848w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.png 1272w, https://substackcdn.com/image/fetch/$s_!sWHs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b81b066-fd02-4e1a-96bf-69a9ca833405_1354x630.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 buttonBase-GK1x3M"><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" class="icon-noB79L"><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 buttonBase-GK1x3M"><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 icon-noB79L"><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="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://blog.maxgale.co/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! 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></channel></rss>