<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>Stark Raving Finkle</title>
		<link>https://starkravingfinkle.org/</link>
		<description>Recent content on Stark Raving Finkle</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Sun, 30 Aug 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://starkravingfinkle.org/feed.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Agentic Engineering: What Agents Can Safely Own</title>
				<link>https://starkravingfinkle.org/posts/2026/08/agentic-eng-what-agents-can-own/</link>
				<pubDate>Sun, 30 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/08/agentic-eng-what-agents-can-own/</guid>
				<description>&lt;p&gt;Two posts back, I used Kent Beck&amp;rsquo;s 3X framework to argue that agentic engineering gets read at different phases of maturity depending on how far you sit from the code. &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/08/agentic-eng-more-code-isnt-enough/&#34;&gt;Last post&lt;/a&gt;, I argued that more code isn&amp;rsquo;t enough. Agents compress execution to nearly free, but judgment is still as critical as ever: deciding what to build, owning intent, understanding the result well enough to steer the next loop.&lt;/p&gt;&#xA;&lt;p&gt;When the cost of coding collapses, lots of other activities become visible. Some of what&amp;rsquo;s left is critical and shouldn&amp;rsquo;t be outsourced: judgment, most of all. The rest is process we built up when writing code was the expensive part and a lot of it was never questioned because the coding cost dwarfed it. That&amp;rsquo;s the part to re-examine.&lt;/p&gt;&#xA;&lt;p&gt;Will Larson &lt;a href=&#34;https://lethain.com/decisions-not-dates/&#34;&gt;landed in the same place from the org side&lt;/a&gt;: very little work is genuinely constrained by execution bandwidth anymore. It&amp;rsquo;s constrained by approvals, by handoffs, and most of all by missing decisions. His prescription is a roadmap of decisions, not dates — stop scheduling software around calendars, and start accelerating the decisions the work is actually waiting on.&lt;/p&gt;&#xA;&lt;p&gt;If that&amp;rsquo;s right, the highest-leverage move isn&amp;rsquo;t making agents faster. It&amp;rsquo;s growing the share of work whose decisions are already closed. The work an agent can pick up and run without stopping to ask what you meant. That&amp;rsquo;s the work that ships in a day. Everything else waits on human decision latency, exactly like it always did. Perhaps that&amp;rsquo;s the bottleneck we should examine.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s focus on a narrow, practical question: &lt;strong&gt;what makes a piece of work agent-shippable, and how do you tell before you hand it over?&lt;/strong&gt; The failure mode here isn&amp;rsquo;t an agent that writes bad code. It&amp;rsquo;s an agent that confidently runs work that was never ready — at volume. Which means the first act of judgment isn&amp;rsquo;t reviewing the output. It&amp;rsquo;s deciding what should have been allowed to run unattended in the first place.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m in the middle of exploring this at work: a real triage, on a real backlog, with real arguments about which traits actually matter. We don&amp;rsquo;t have a playbook for this yet, so I&amp;rsquo;ll revise and update as we go. We&amp;rsquo;re starting with a set of criteria we&amp;rsquo;re testing against the work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-right-conditions-arent-new&#34;&gt;The Right Conditions Aren&amp;rsquo;t New&lt;/h2&gt;&#xA;&lt;p&gt;Same-day start-to-deploy was always possible, but only for work with a specific profile:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Clear requirements, or a problem the developer already understood&lt;/li&gt;&#xA;&lt;li&gt;Small scope&lt;/li&gt;&#xA;&lt;li&gt;An unambiguous definition of done — no second-guessing&lt;/li&gt;&#xA;&lt;li&gt;The developer already held the model of the code — the frameworks, systems, and patterns&lt;/li&gt;&#xA;&lt;li&gt;The developer had everything needed to build, test, and verify&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Every one of those is a proxy for the same thing: reducing the person&amp;rsquo;s thinking-and-typing load. The constraint before agents was &lt;strong&gt;code generation&lt;/strong&gt;: how fast one person could hold a problem in their head and produce correct code. All five traits focus on that constraint. Small scope shrinks it. Clear requirements remove upstream thinking. Existing context removes ramp-up.&lt;/p&gt;&#xA;&lt;p&gt;Two things were bundled inside that one person, and they&amp;rsquo;re the whole reason the profile is changing:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;The generator was also the decider.&lt;/strong&gt; Small judgment calls got made in-flight, at zero latency, by the same person writing the code. Nobody wrote them down, because nobody had to.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Verification was partly internal.&lt;/strong&gt; An experienced developer carries a running &amp;ldquo;this looks wrong&amp;rdquo; sense; formal checks only had to cover the gap that instinct didn&amp;rsquo;t.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Give code generation to an agent and those two split apart. The in-flight decisions now have to be written down as intent or context, because the agent won&amp;rsquo;t make them the way you would. The internal &amp;ldquo;this looks wrong&amp;rdquo; has to be externalized as mechanical checks, because the agent doesn&amp;rsquo;t have it. This is why the intent and verification pillars from the last post stop being nice-to-haves. They existed in someone&amp;rsquo;s head, and now they can&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;When a developer holds the model of the code, it&amp;rsquo;s really two things. The first part is &lt;strong&gt;judgment&lt;/strong&gt;: knowing, from principle, which approach is right when the situation is new. That stays with the developer. The other part is &lt;strong&gt;patterns&lt;/strong&gt;: knowing how &lt;em&gt;this&lt;/em&gt; codebase does a thing, what the established shape of a change looks like here. Patterns are extractable. Judgment isn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;Which makes &lt;strong&gt;Patterned&lt;/strong&gt; a first-class condition that we can leverage with agents. The second instance of anything is cheap, because now there&amp;rsquo;s a pattern to copy; the first instance is where the judgment comes into play. So &lt;strong&gt;build the first instance deliberately and slowly, then fast-track the rest.&lt;/strong&gt; The first one is you deciding the shape, the rest is the agent matching it.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;An agent can go exactly as fast as your feedback loop allows.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;That&amp;rsquo;s also why the same model produces excellent output in one repository and slop in another. &lt;strong&gt;Agent-shippability is a property of the codebase, not of the model.&lt;/strong&gt; It&amp;rsquo;s something you build, not something you buy. Which is also why swapping harnesses isn&amp;rsquo;t the silver bullet.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-qualifies-now&#34;&gt;What Qualifies Now&lt;/h2&gt;&#xA;&lt;p&gt;A unit of work can be started and deployed in a day by an agent when it&amp;rsquo;s:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Decision-closed.&lt;/strong&gt; No judgment calls remain in-flight, or they&amp;rsquo;re pre-encoded as written policy the agent can consult.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mechanically verifiable.&lt;/strong&gt; &amp;ldquo;Done&amp;rdquo; is checkable by a machine, not by taste, not by a human reading the diff carefully.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Blast-radius-bounded.&lt;/strong&gt; Cheaply reversible, behind a flag, an additive migration, an isolated module.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context-resident.&lt;/strong&gt; Everything the agent needs will physically fit in what it can take in.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Patterned.&lt;/strong&gt; The change matches a shape already in the repo, so the agent has examples to copy rather than a principle to work out.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Those five are all properties of the work. There&amp;rsquo;s one more, called out in Larson&amp;rsquo;s post, that&amp;rsquo;s a property of what stands &lt;em&gt;around&lt;/em&gt; the work: &lt;strong&gt;approval-clear&lt;/strong&gt;, meaning no human-latency gate sits between merge and deploy. A change can be decision-closed and patterned and still not ship today because it&amp;rsquo;s waiting on a security sign-off or another team&amp;rsquo;s review.&lt;/p&gt;&#xA;&lt;p&gt;This list is an all-or-nothing requirement. Work must clear every single trait to qualify, not just hit a good average. One specific pairing often trips people up because two of these traits seem completely unrelated. &lt;strong&gt;Cognitive load and blast radius are independent&lt;/strong&gt;. Adding an index to a 400-million-row table, or changing persisted data, could be trivially simple: minimum thinking, maximum clarity — and also a one-way door. &amp;ldquo;It&amp;rsquo;s a small change&amp;rdquo; is an argument about cognitive load; it says nothing about whether you can revert it. Decision-closed and patterned can both be true while blast-radius-bounded is flatly false. The work that feels safest to wave through is often exactly the work that isn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ambiguity-is-a-state-you-can-change&#34;&gt;Ambiguity Is a State You Can Change&lt;/h2&gt;&#xA;&lt;p&gt;Reading the list and it sounds like work either qualifies or it doesn&amp;rsquo;t. Sort the backlog into two piles and hand one to the agents. But the most valuable work &lt;em&gt;almost never&lt;/em&gt; starts in the qualifying pile. Interesting work is ambiguous work: the decisions aren&amp;rsquo;t closed, the definition of done is soft, nobody&amp;rsquo;s holding the full model yet. If you only ever fast-track what&amp;rsquo;s already decision-closed, you&amp;rsquo;ve capped the payoff and it&amp;rsquo;s usually the least interesting work you have.&lt;/p&gt;&#xA;&lt;p&gt;The trap is treating &lt;em&gt;decision-closed&lt;/em&gt; as a fixed property the work. It isn&amp;rsquo;t. It&amp;rsquo;s a state you can move work into, and the move is cheaper now than it&amp;rsquo;s ever been.&lt;/p&gt;&#xA;&lt;p&gt;Larson calls this the iterative application of judgment: you don&amp;rsquo;t resolve a hard, ambiguous decision by thinking harder about it, you resolve it by prototyping until the options collapse. The answers get obvious not because you got smarter but because a working artifact makes the constraints explicit and eliminates the paths that don&amp;rsquo;t survive contact with them. One big fuzzy decision becomes a series of small clear ones. That&amp;rsquo;s decision latency attacked at the source, not by deciding faster, but by making the decision easy.&lt;/p&gt;&#xA;&lt;p&gt;Prototyping was always the right answer to ambiguity. It was just expensive enough that most teams didn&amp;rsquo;t have time for it. That&amp;rsquo;s the part that changed. &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/04/coding-agents-experimentation-velocity/&#34;&gt;I&amp;rsquo;ve written before&lt;/a&gt; about how agents turn a multi-week probe into an afternoon one. Discovery phases that used to produce documents now produce working scaffolding, and a POC can unblock an architecture decision in days instead of weeks. When a prototype costs an afternoon, &amp;ldquo;prototype until the decision closes&amp;rdquo; stops being a luxury and becomes the default way you qualify ambiguous work.&lt;/p&gt;&#xA;&lt;p&gt;So there are two ways a unit of work becomes agent-shippable. It was already decision-closed, rare, and usually the boring stuff. Or you spend a person&amp;rsquo;s judgment up front, cheaply, to close it: prototype the ambiguity, encode what you learned as the constraints the agent will follow, and &lt;em&gt;then&lt;/em&gt; fast-track the rest. It&amp;rsquo;s the same as creating a pattern, except here the first instance is the thinking, not the code.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;The share of your backlog that qualifies isn&amp;rsquo;t a number you measure. It&amp;rsquo;s a number you grow by spending cheap, up-front judgment to move work across the line.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;One caveat, because this is exactly where enthusiasm gets people hurt: a prototype that closes the decision is not the thing you ship. The artifact you built to think with hasn&amp;rsquo;t been verified, isn&amp;rsquo;t reversible, and hasn&amp;rsquo;t earned the other traits just because it answered the question. Closing the decision qualifies work to &lt;em&gt;enter&lt;/em&gt; the fast path — it doesn&amp;rsquo;t let it skip the path. The moment a POC that &amp;ldquo;works&amp;rdquo; gets rushed straight to production is the moment you&amp;rsquo;ve traded the discipline for speed.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-number-worth-growing&#34;&gt;The Number Worth Growing&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/&#34;&gt;Post one&lt;/a&gt; made fun of &amp;ldquo;percentage of code written by AI&amp;rdquo; as a KPI — a gameable metric that inflates the exact review burden that&amp;rsquo;s already the bottleneck. Here&amp;rsquo;s a metric that isn&amp;rsquo;t gameable: &lt;strong&gt;qualification rate&lt;/strong&gt; — the fraction of incoming work that clears the bar to run agent-shippable, trending up over time.&lt;/p&gt;&#xA;&lt;p&gt;You can&amp;rsquo;t fake it by generating more code. The only way it moves is by doing the work underneath it: closing decisions before they reach the agent, building the first instance so the next ten are patterned, tightening verification until &amp;ldquo;done&amp;rdquo; is machine-checkable, keeping blast radius bounded. Every one of those is a piece of judgment spent up front.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s also a decent answer to the executive question from that first post: &lt;em&gt;is this working?&lt;/em&gt; Not &amp;ldquo;how much code did agents write,&amp;rdquo; but &amp;ldquo;how much more of our work is now the kind of work an agent can safely own than was six months ago?&amp;rdquo; That number going up is a real transformation.&lt;/p&gt;&#xA;&lt;p&gt;Once you can classify work, and grow the share of work that qualifies, you need a system that actually routes it. Send qualified work onto an agent-fast path, everything else onto the slower, more mindful one, with judgement and decisions. That&amp;rsquo;s how we get to really start Expanding and Extracting value.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Two posts back, I used Kent Beck&amp;rsquo;s 3X framework to argue that agentic engineering gets read at different phases of maturity depending on how far you sit from the code. &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/08/agentic-eng-more-code-isnt-enough/&#34;&gt;Last post&lt;/a&gt;, I argued that more code isn&amp;rsquo;t enough. Agents compress execution to nearly free, but judgment is still as critical as ever: deciding what to build, owning intent, understanding the result well enough to steer the next loop.&lt;/p&gt;&#xA;&lt;p&gt;When the cost of coding collapses, lots of other activities become visible. Some of what&amp;rsquo;s left is critical and shouldn&amp;rsquo;t be outsourced: judgment, most of all. The rest is process we built up when writing code was the expensive part and a lot of it was never questioned because the coding cost dwarfed it. That&amp;rsquo;s the part to re-examine.&lt;/p&gt;&#xA;&lt;p&gt;Will Larson &lt;a href=&#34;https://lethain.com/decisions-not-dates/&#34;&gt;landed in the same place from the org side&lt;/a&gt;: very little work is genuinely constrained by execution bandwidth anymore. It&amp;rsquo;s constrained by approvals, by handoffs, and most of all by missing decisions. His prescription is a roadmap of decisions, not dates — stop scheduling software around calendars, and start accelerating the decisions the work is actually waiting on.&lt;/p&gt;&#xA;&lt;p&gt;If that&amp;rsquo;s right, the highest-leverage move isn&amp;rsquo;t making agents faster. It&amp;rsquo;s growing the share of work whose decisions are already closed. The work an agent can pick up and run without stopping to ask what you meant. That&amp;rsquo;s the work that ships in a day. Everything else waits on human decision latency, exactly like it always did. Perhaps that&amp;rsquo;s the bottleneck we should examine.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s focus on a narrow, practical question: &lt;strong&gt;what makes a piece of work agent-shippable, and how do you tell before you hand it over?&lt;/strong&gt; The failure mode here isn&amp;rsquo;t an agent that writes bad code. It&amp;rsquo;s an agent that confidently runs work that was never ready — at volume. Which means the first act of judgment isn&amp;rsquo;t reviewing the output. It&amp;rsquo;s deciding what should have been allowed to run unattended in the first place.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m in the middle of exploring this at work: a real triage, on a real backlog, with real arguments about which traits actually matter. We don&amp;rsquo;t have a playbook for this yet, so I&amp;rsquo;ll revise and update as we go. We&amp;rsquo;re starting with a set of criteria we&amp;rsquo;re testing against the work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-right-conditions-arent-new&#34;&gt;The Right Conditions Aren&amp;rsquo;t New&lt;/h2&gt;&#xA;&lt;p&gt;Same-day start-to-deploy was always possible, but only for work with a specific profile:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Clear requirements, or a problem the developer already understood&lt;/li&gt;&#xA;&lt;li&gt;Small scope&lt;/li&gt;&#xA;&lt;li&gt;An unambiguous definition of done — no second-guessing&lt;/li&gt;&#xA;&lt;li&gt;The developer already held the model of the code — the frameworks, systems, and patterns&lt;/li&gt;&#xA;&lt;li&gt;The developer had everything needed to build, test, and verify&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Every one of those is a proxy for the same thing: reducing the person&amp;rsquo;s thinking-and-typing load. The constraint before agents was &lt;strong&gt;code generation&lt;/strong&gt;: how fast one person could hold a problem in their head and produce correct code. All five traits focus on that constraint. Small scope shrinks it. Clear requirements remove upstream thinking. Existing context removes ramp-up.&lt;/p&gt;&#xA;&lt;p&gt;Two things were bundled inside that one person, and they&amp;rsquo;re the whole reason the profile is changing:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;The generator was also the decider.&lt;/strong&gt; Small judgment calls got made in-flight, at zero latency, by the same person writing the code. Nobody wrote them down, because nobody had to.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Verification was partly internal.&lt;/strong&gt; An experienced developer carries a running &amp;ldquo;this looks wrong&amp;rdquo; sense; formal checks only had to cover the gap that instinct didn&amp;rsquo;t.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Give code generation to an agent and those two split apart. The in-flight decisions now have to be written down as intent or context, because the agent won&amp;rsquo;t make them the way you would. The internal &amp;ldquo;this looks wrong&amp;rdquo; has to be externalized as mechanical checks, because the agent doesn&amp;rsquo;t have it. This is why the intent and verification pillars from the last post stop being nice-to-haves. They existed in someone&amp;rsquo;s head, and now they can&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;When a developer holds the model of the code, it&amp;rsquo;s really two things. The first part is &lt;strong&gt;judgment&lt;/strong&gt;: knowing, from principle, which approach is right when the situation is new. That stays with the developer. The other part is &lt;strong&gt;patterns&lt;/strong&gt;: knowing how &lt;em&gt;this&lt;/em&gt; codebase does a thing, what the established shape of a change looks like here. Patterns are extractable. Judgment isn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;Which makes &lt;strong&gt;Patterned&lt;/strong&gt; a first-class condition that we can leverage with agents. The second instance of anything is cheap, because now there&amp;rsquo;s a pattern to copy; the first instance is where the judgment comes into play. So &lt;strong&gt;build the first instance deliberately and slowly, then fast-track the rest.&lt;/strong&gt; The first one is you deciding the shape, the rest is the agent matching it.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;An agent can go exactly as fast as your feedback loop allows.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;That&amp;rsquo;s also why the same model produces excellent output in one repository and slop in another. &lt;strong&gt;Agent-shippability is a property of the codebase, not of the model.&lt;/strong&gt; It&amp;rsquo;s something you build, not something you buy. Which is also why swapping harnesses isn&amp;rsquo;t the silver bullet.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-qualifies-now&#34;&gt;What Qualifies Now&lt;/h2&gt;&#xA;&lt;p&gt;A unit of work can be started and deployed in a day by an agent when it&amp;rsquo;s:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Decision-closed.&lt;/strong&gt; No judgment calls remain in-flight, or they&amp;rsquo;re pre-encoded as written policy the agent can consult.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mechanically verifiable.&lt;/strong&gt; &amp;ldquo;Done&amp;rdquo; is checkable by a machine, not by taste, not by a human reading the diff carefully.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Blast-radius-bounded.&lt;/strong&gt; Cheaply reversible, behind a flag, an additive migration, an isolated module.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context-resident.&lt;/strong&gt; Everything the agent needs will physically fit in what it can take in.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Patterned.&lt;/strong&gt; The change matches a shape already in the repo, so the agent has examples to copy rather than a principle to work out.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Those five are all properties of the work. There&amp;rsquo;s one more, called out in Larson&amp;rsquo;s post, that&amp;rsquo;s a property of what stands &lt;em&gt;around&lt;/em&gt; the work: &lt;strong&gt;approval-clear&lt;/strong&gt;, meaning no human-latency gate sits between merge and deploy. A change can be decision-closed and patterned and still not ship today because it&amp;rsquo;s waiting on a security sign-off or another team&amp;rsquo;s review.&lt;/p&gt;&#xA;&lt;p&gt;This list is an all-or-nothing requirement. Work must clear every single trait to qualify, not just hit a good average. One specific pairing often trips people up because two of these traits seem completely unrelated. &lt;strong&gt;Cognitive load and blast radius are independent&lt;/strong&gt;. Adding an index to a 400-million-row table, or changing persisted data, could be trivially simple: minimum thinking, maximum clarity — and also a one-way door. &amp;ldquo;It&amp;rsquo;s a small change&amp;rdquo; is an argument about cognitive load; it says nothing about whether you can revert it. Decision-closed and patterned can both be true while blast-radius-bounded is flatly false. The work that feels safest to wave through is often exactly the work that isn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;h2 id=&#34;ambiguity-is-a-state-you-can-change&#34;&gt;Ambiguity Is a State You Can Change&lt;/h2&gt;&#xA;&lt;p&gt;Reading the list and it sounds like work either qualifies or it doesn&amp;rsquo;t. Sort the backlog into two piles and hand one to the agents. But the most valuable work &lt;em&gt;almost never&lt;/em&gt; starts in the qualifying pile. Interesting work is ambiguous work: the decisions aren&amp;rsquo;t closed, the definition of done is soft, nobody&amp;rsquo;s holding the full model yet. If you only ever fast-track what&amp;rsquo;s already decision-closed, you&amp;rsquo;ve capped the payoff and it&amp;rsquo;s usually the least interesting work you have.&lt;/p&gt;&#xA;&lt;p&gt;The trap is treating &lt;em&gt;decision-closed&lt;/em&gt; as a fixed property the work. It isn&amp;rsquo;t. It&amp;rsquo;s a state you can move work into, and the move is cheaper now than it&amp;rsquo;s ever been.&lt;/p&gt;&#xA;&lt;p&gt;Larson calls this the iterative application of judgment: you don&amp;rsquo;t resolve a hard, ambiguous decision by thinking harder about it, you resolve it by prototyping until the options collapse. The answers get obvious not because you got smarter but because a working artifact makes the constraints explicit and eliminates the paths that don&amp;rsquo;t survive contact with them. One big fuzzy decision becomes a series of small clear ones. That&amp;rsquo;s decision latency attacked at the source, not by deciding faster, but by making the decision easy.&lt;/p&gt;&#xA;&lt;p&gt;Prototyping was always the right answer to ambiguity. It was just expensive enough that most teams didn&amp;rsquo;t have time for it. That&amp;rsquo;s the part that changed. &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/04/coding-agents-experimentation-velocity/&#34;&gt;I&amp;rsquo;ve written before&lt;/a&gt; about how agents turn a multi-week probe into an afternoon one. Discovery phases that used to produce documents now produce working scaffolding, and a POC can unblock an architecture decision in days instead of weeks. When a prototype costs an afternoon, &amp;ldquo;prototype until the decision closes&amp;rdquo; stops being a luxury and becomes the default way you qualify ambiguous work.&lt;/p&gt;&#xA;&lt;p&gt;So there are two ways a unit of work becomes agent-shippable. It was already decision-closed, rare, and usually the boring stuff. Or you spend a person&amp;rsquo;s judgment up front, cheaply, to close it: prototype the ambiguity, encode what you learned as the constraints the agent will follow, and &lt;em&gt;then&lt;/em&gt; fast-track the rest. It&amp;rsquo;s the same as creating a pattern, except here the first instance is the thinking, not the code.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;The share of your backlog that qualifies isn&amp;rsquo;t a number you measure. It&amp;rsquo;s a number you grow by spending cheap, up-front judgment to move work across the line.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;One caveat, because this is exactly where enthusiasm gets people hurt: a prototype that closes the decision is not the thing you ship. The artifact you built to think with hasn&amp;rsquo;t been verified, isn&amp;rsquo;t reversible, and hasn&amp;rsquo;t earned the other traits just because it answered the question. Closing the decision qualifies work to &lt;em&gt;enter&lt;/em&gt; the fast path — it doesn&amp;rsquo;t let it skip the path. The moment a POC that &amp;ldquo;works&amp;rdquo; gets rushed straight to production is the moment you&amp;rsquo;ve traded the discipline for speed.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-number-worth-growing&#34;&gt;The Number Worth Growing&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/&#34;&gt;Post one&lt;/a&gt; made fun of &amp;ldquo;percentage of code written by AI&amp;rdquo; as a KPI — a gameable metric that inflates the exact review burden that&amp;rsquo;s already the bottleneck. Here&amp;rsquo;s a metric that isn&amp;rsquo;t gameable: &lt;strong&gt;qualification rate&lt;/strong&gt; — the fraction of incoming work that clears the bar to run agent-shippable, trending up over time.&lt;/p&gt;&#xA;&lt;p&gt;You can&amp;rsquo;t fake it by generating more code. The only way it moves is by doing the work underneath it: closing decisions before they reach the agent, building the first instance so the next ten are patterned, tightening verification until &amp;ldquo;done&amp;rdquo; is machine-checkable, keeping blast radius bounded. Every one of those is a piece of judgment spent up front.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s also a decent answer to the executive question from that first post: &lt;em&gt;is this working?&lt;/em&gt; Not &amp;ldquo;how much code did agents write,&amp;rdquo; but &amp;ldquo;how much more of our work is now the kind of work an agent can safely own than was six months ago?&amp;rdquo; That number going up is a real transformation.&lt;/p&gt;&#xA;&lt;p&gt;Once you can classify work, and grow the share of work that qualifies, you need a system that actually routes it. Send qualified work onto an agent-fast path, everything else onto the slower, more mindful one, with judgement and decisions. That&amp;rsquo;s how we get to really start Expanding and Extracting value.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Agentic Engineering: More Code Isn&#39;t Enough</title>
				<link>https://starkravingfinkle.org/posts/2026/08/agentic-eng-more-code-isnt-enough/</link>
				<pubDate>Sat, 15 Aug 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/08/agentic-eng-more-code-isnt-enough/</guid>
				<description>&lt;p&gt;In my &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/&#34;&gt;last post&lt;/a&gt; using Kent Beck&amp;rsquo;s 3X Framework, I argued that agentic engineering is being perceived at different phases of maturity depending on where you sit. Executives believe the systems are mature, in Extract. Practitioners are doing the work of Expand. The practice, the tools, the workflows are still churning in Explore.&lt;/p&gt;&#xA;&lt;p&gt;Underneath that churn, there are four foundations we know provide value:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;: What the agent needs to know about the situation — the codebase, the coding patterns, the how-tos for the frameworks and systems we already have.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Intent&lt;/strong&gt;: What we want the agent to do and why. The clarity of the intention drives the quality of the outcome. Garbage in, garbage out. So much of developer craft is moving to this process.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code generation&lt;/strong&gt;: Turning context and intent into working code. The most obvious win, especially with developer-steered agentic coding sessions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code review and approval&lt;/strong&gt;: Checking that the generated code is correct, consistent, and safe to merge. Agentic code reviews are very good, but removing people from the process creates anxiety.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Each of those made it through &lt;em&gt;Explore&lt;/em&gt; and these are the baseline for any engineering organization. Developers are definitely able to &lt;em&gt;Extract&lt;/em&gt; value and deliver work faster using these foundations.&lt;/p&gt;&#xA;&lt;p&gt;If you aren&amp;rsquo;t succeeding with developer-steered agentic coding sessions, you aren&amp;rsquo;t ready to move on to the next level. Even if you are successful at creating more code, you still need to figure out how to turn that increased code into increased value.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s tempting to head toward the &amp;ldquo;dark factory&amp;rdquo; model: agents running 24/7, writing more and more code. But ask the pre-agentic version of that question: if we hired three times as many developers and set them loose writing code, would that create three times the value? We already know it wouldn&amp;rsquo;t. Code production was always the easy part to blame for not increasing business value. Agents removed code production as the bottleneck, so now you more easily see how planning, decisions, judgement, and understanding are the real bottlenecks.&lt;/p&gt;&#xA;&lt;h2 id=&#34;get-good-at-the-basics&#34;&gt;Get Good at the Basics&lt;/h2&gt;&#xA;&lt;p&gt;Getting good at the four pillars with developer-steered sessions — a human in the loop, driving — is a critical milestone and shouldn&amp;rsquo;t be skipped. Context that yields code matching your patterns. Intent sharp enough that the agent builds the right thing. Generation you can steer. Reviews, human and agent, that catch what your experts would.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s the org-level version of something from the last post: autonomy is earned, not granted. If developer-steered coding isn&amp;rsquo;t reliably producing work you&amp;rsquo;d actually ship, then dark factories and features-in-a-day are fantasies. You&amp;rsquo;d only be scaling something that doesn&amp;rsquo;t work yet and yields no value. You&amp;rsquo;re ready when a developer and an agent reliably turn a clear intent into shippable, reviewed work &lt;em&gt;and the developer still understands what came out&lt;/em&gt; — not when the agent simply produced a lot. Once you hit this milestone, you&amp;rsquo;re able to separate the hype from the actual work needed to level-up.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-part-agents-cant-do&#34;&gt;The Part Agents Can&amp;rsquo;t Do&lt;/h2&gt;&#xA;&lt;p&gt;Say you&amp;rsquo;ve got all four pillars humming. An agent can now execute every one of them. It still won&amp;rsquo;t produce business or customer value on its own because the value was never purely in the code.&lt;/p&gt;&#xA;&lt;p&gt;Writing code was always the smaller part of the job; one line of research puts developers at &lt;a href=&#34;https://newsletter.getdx.com/p/measuring-the-impact-of-ai-coding&#34;&gt;roughly 14% of the week&lt;/a&gt; actually writing code. Agents took over that slice and made it nearly free. Deciding what&amp;rsquo;s worth building, shaping why, judging whether the result is right and what it means for the next move — that is the part that turns code generation into value, and it&amp;rsquo;s the part that doesn&amp;rsquo;t really automate.&lt;/p&gt;&#xA;&lt;p&gt;Three activities still depend on people, and they&amp;rsquo;re all the same shape:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;What to build.&lt;/strong&gt; Direction. An agent optimizes toward a target it&amp;rsquo;s handed; it doesn&amp;rsquo;t decide the target is worth hitting.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Driving intent.&lt;/strong&gt; The clarity of the why drives the quality of the outcome. You don&amp;rsquo;t hand off authorship of intent, you participate in it.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Understanding the result well enough to steer the next loop.&lt;/strong&gt; Not a rubber stamp on generated code. A working model of the system, at the architectural level, good enough to decide what happens next.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Some people really want to ignore or downplay the importance of &lt;em&gt;understanding&lt;/em&gt;. Geoffrey Litt describes &lt;a href=&#34;https://www.geoffreylitt.com/2026/07/02/understanding-is-the-new-bottleneck&#34;&gt;understanding as a new bottleneck&lt;/a&gt;: there are two reasons to understand an agent&amp;rsquo;s code. You can understand it well enough to &lt;em&gt;evaluate&lt;/em&gt; (thumbs up, thumbs down) or well enough to &lt;em&gt;participate&lt;/em&gt;, to hold a model of the system that lets you decide what comes next. Agents are getting good at supporting evaluation. Participation is the part that keeps you valuable, because a project is never one loop, it&amp;rsquo;s many. Participation is what lets you consider feedback from the existing loop and drive the direction of the next one.&lt;/p&gt;&#xA;&lt;p&gt;Evaluation-only comprehension feels like rigor and good process, but we know that it becomes mechanical especially when under time pressure or volume. You can approve a correctly compiling diff every day and understand the system a little less each time. Margaret-Anne Storey names what accrues &lt;a href=&#34;https://queue.acm.org/detail.cfm?id=3807966&#34;&gt;an &amp;ldquo;accumulation of not knowing&amp;rdquo;&lt;/a&gt; — cognitive debt, and it&amp;rsquo;s the debt you can run up while every check stays green. The model in your head becomes less clear, and at some point you can&amp;rsquo;t drive intent anymore because intent is more than &amp;ldquo;is this correct,&amp;rdquo; it&amp;rsquo;s &amp;ldquo;why this approach should be used and not the alternatives.&amp;rdquo;&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-this-sets-up&#34;&gt;What This Sets Up&lt;/h2&gt;&#xA;&lt;p&gt;So &amp;ldquo;more code isn&amp;rsquo;t enough&amp;rdquo; is a constraint statement. Agents made generation nearly free, and none of the critical thinking parts got faster or easier. Don&amp;rsquo;t confuse moving code faster for progress if it doesn&amp;rsquo;t create value for the business or its customers. It&amp;rsquo;s just code churn.&lt;/p&gt;&#xA;&lt;p&gt;Even the people whose job is measuring this draw the same line. DX frames the goal as capacity, not horsepower, and is careful to wall off &lt;em&gt;engineering&lt;/em&gt; capacity from &lt;em&gt;business&lt;/em&gt; value, noting the far side is often not an engineering question at all. This post is about that far side. The judgment that turns capacity into value is exactly what an agent can&amp;rsquo;t do for you.&lt;/p&gt;&#xA;&lt;p&gt;That human work isn&amp;rsquo;t a tax on velocity. &lt;strong&gt;The output of the high-judgment work is what makes agent volume safe and valuable in the first place&lt;/strong&gt;. The decision making, pattern creation, and system understanding have clearly become the true craft. Understanding compounds and builds into &lt;em&gt;value&lt;/em&gt; throughput.&lt;/p&gt;&#xA;&lt;p&gt;That leaves three questions: what qualifies for the fast path, what has to stay on the slow one, and what we owe the people who need to keep understanding a system they&amp;rsquo;re no longer reading line by line.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;In my &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/&#34;&gt;last post&lt;/a&gt; using Kent Beck&amp;rsquo;s 3X Framework, I argued that agentic engineering is being perceived at different phases of maturity depending on where you sit. Executives believe the systems are mature, in Extract. Practitioners are doing the work of Expand. The practice, the tools, the workflows are still churning in Explore.&lt;/p&gt;&#xA;&lt;p&gt;Underneath that churn, there are four foundations we know provide value:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt;: What the agent needs to know about the situation — the codebase, the coding patterns, the how-tos for the frameworks and systems we already have.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Intent&lt;/strong&gt;: What we want the agent to do and why. The clarity of the intention drives the quality of the outcome. Garbage in, garbage out. So much of developer craft is moving to this process.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code generation&lt;/strong&gt;: Turning context and intent into working code. The most obvious win, especially with developer-steered agentic coding sessions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code review and approval&lt;/strong&gt;: Checking that the generated code is correct, consistent, and safe to merge. Agentic code reviews are very good, but removing people from the process creates anxiety.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Each of those made it through &lt;em&gt;Explore&lt;/em&gt; and these are the baseline for any engineering organization. Developers are definitely able to &lt;em&gt;Extract&lt;/em&gt; value and deliver work faster using these foundations.&lt;/p&gt;&#xA;&lt;p&gt;If you aren&amp;rsquo;t succeeding with developer-steered agentic coding sessions, you aren&amp;rsquo;t ready to move on to the next level. Even if you are successful at creating more code, you still need to figure out how to turn that increased code into increased value.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s tempting to head toward the &amp;ldquo;dark factory&amp;rdquo; model: agents running 24/7, writing more and more code. But ask the pre-agentic version of that question: if we hired three times as many developers and set them loose writing code, would that create three times the value? We already know it wouldn&amp;rsquo;t. Code production was always the easy part to blame for not increasing business value. Agents removed code production as the bottleneck, so now you more easily see how planning, decisions, judgement, and understanding are the real bottlenecks.&lt;/p&gt;&#xA;&lt;h2 id=&#34;get-good-at-the-basics&#34;&gt;Get Good at the Basics&lt;/h2&gt;&#xA;&lt;p&gt;Getting good at the four pillars with developer-steered sessions — a human in the loop, driving — is a critical milestone and shouldn&amp;rsquo;t be skipped. Context that yields code matching your patterns. Intent sharp enough that the agent builds the right thing. Generation you can steer. Reviews, human and agent, that catch what your experts would.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s the org-level version of something from the last post: autonomy is earned, not granted. If developer-steered coding isn&amp;rsquo;t reliably producing work you&amp;rsquo;d actually ship, then dark factories and features-in-a-day are fantasies. You&amp;rsquo;d only be scaling something that doesn&amp;rsquo;t work yet and yields no value. You&amp;rsquo;re ready when a developer and an agent reliably turn a clear intent into shippable, reviewed work &lt;em&gt;and the developer still understands what came out&lt;/em&gt; — not when the agent simply produced a lot. Once you hit this milestone, you&amp;rsquo;re able to separate the hype from the actual work needed to level-up.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-part-agents-cant-do&#34;&gt;The Part Agents Can&amp;rsquo;t Do&lt;/h2&gt;&#xA;&lt;p&gt;Say you&amp;rsquo;ve got all four pillars humming. An agent can now execute every one of them. It still won&amp;rsquo;t produce business or customer value on its own because the value was never purely in the code.&lt;/p&gt;&#xA;&lt;p&gt;Writing code was always the smaller part of the job; one line of research puts developers at &lt;a href=&#34;https://newsletter.getdx.com/p/measuring-the-impact-of-ai-coding&#34;&gt;roughly 14% of the week&lt;/a&gt; actually writing code. Agents took over that slice and made it nearly free. Deciding what&amp;rsquo;s worth building, shaping why, judging whether the result is right and what it means for the next move — that is the part that turns code generation into value, and it&amp;rsquo;s the part that doesn&amp;rsquo;t really automate.&lt;/p&gt;&#xA;&lt;p&gt;Three activities still depend on people, and they&amp;rsquo;re all the same shape:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;What to build.&lt;/strong&gt; Direction. An agent optimizes toward a target it&amp;rsquo;s handed; it doesn&amp;rsquo;t decide the target is worth hitting.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Driving intent.&lt;/strong&gt; The clarity of the why drives the quality of the outcome. You don&amp;rsquo;t hand off authorship of intent, you participate in it.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Understanding the result well enough to steer the next loop.&lt;/strong&gt; Not a rubber stamp on generated code. A working model of the system, at the architectural level, good enough to decide what happens next.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Some people really want to ignore or downplay the importance of &lt;em&gt;understanding&lt;/em&gt;. Geoffrey Litt describes &lt;a href=&#34;https://www.geoffreylitt.com/2026/07/02/understanding-is-the-new-bottleneck&#34;&gt;understanding as a new bottleneck&lt;/a&gt;: there are two reasons to understand an agent&amp;rsquo;s code. You can understand it well enough to &lt;em&gt;evaluate&lt;/em&gt; (thumbs up, thumbs down) or well enough to &lt;em&gt;participate&lt;/em&gt;, to hold a model of the system that lets you decide what comes next. Agents are getting good at supporting evaluation. Participation is the part that keeps you valuable, because a project is never one loop, it&amp;rsquo;s many. Participation is what lets you consider feedback from the existing loop and drive the direction of the next one.&lt;/p&gt;&#xA;&lt;p&gt;Evaluation-only comprehension feels like rigor and good process, but we know that it becomes mechanical especially when under time pressure or volume. You can approve a correctly compiling diff every day and understand the system a little less each time. Margaret-Anne Storey names what accrues &lt;a href=&#34;https://queue.acm.org/detail.cfm?id=3807966&#34;&gt;an &amp;ldquo;accumulation of not knowing&amp;rdquo;&lt;/a&gt; — cognitive debt, and it&amp;rsquo;s the debt you can run up while every check stays green. The model in your head becomes less clear, and at some point you can&amp;rsquo;t drive intent anymore because intent is more than &amp;ldquo;is this correct,&amp;rdquo; it&amp;rsquo;s &amp;ldquo;why this approach should be used and not the alternatives.&amp;rdquo;&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-this-sets-up&#34;&gt;What This Sets Up&lt;/h2&gt;&#xA;&lt;p&gt;So &amp;ldquo;more code isn&amp;rsquo;t enough&amp;rdquo; is a constraint statement. Agents made generation nearly free, and none of the critical thinking parts got faster or easier. Don&amp;rsquo;t confuse moving code faster for progress if it doesn&amp;rsquo;t create value for the business or its customers. It&amp;rsquo;s just code churn.&lt;/p&gt;&#xA;&lt;p&gt;Even the people whose job is measuring this draw the same line. DX frames the goal as capacity, not horsepower, and is careful to wall off &lt;em&gt;engineering&lt;/em&gt; capacity from &lt;em&gt;business&lt;/em&gt; value, noting the far side is often not an engineering question at all. This post is about that far side. The judgment that turns capacity into value is exactly what an agent can&amp;rsquo;t do for you.&lt;/p&gt;&#xA;&lt;p&gt;That human work isn&amp;rsquo;t a tax on velocity. &lt;strong&gt;The output of the high-judgment work is what makes agent volume safe and valuable in the first place&lt;/strong&gt;. The decision making, pattern creation, and system understanding have clearly become the true craft. Understanding compounds and builds into &lt;em&gt;value&lt;/em&gt; throughput.&lt;/p&gt;&#xA;&lt;p&gt;That leaves three questions: what qualifies for the fast path, what has to stay on the slow one, and what we owe the people who need to keep understanding a system they&amp;rsquo;re no longer reading line by line.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Agentic Engineering: Where&#39;s the Playbook?</title>
				<link>https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/</link>
				<pubDate>Sun, 19 Jul 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/07/agentic-eng-wheres-the-playbook/</guid>
				<description>&lt;p&gt;Agentic engineering is happening at a breakneck pace, and many are wondering if they are making the &amp;ldquo;right&amp;rdquo; decisions and using the &amp;ldquo;right&amp;rdquo; approaches. I&amp;rsquo;ve been a proponent of not chasing the newest fad, but to instead learn about what&amp;rsquo;s being used across the industry and keeping your options open.&lt;/p&gt;&#xA;&lt;p&gt;I was listening to a recent Pragmatic Engineer &lt;a href=&#34;https://newsletter.pragmaticengineer.com/p/how-kent-beck-shapes-the-software&#34;&gt;podcast&lt;/a&gt;, where Gergely was interviewing Kent Beck. Kent has spent decades helping to shape software development through a variety of methodologies. Toward the end of the podcast, Kent describes our current situation with agentic development: &lt;strong&gt;We don&amp;rsquo;t have a playbook yet.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;We don&amp;rsquo;t have a playbook. But we do have a framework for what it feels like to not have one, and Beck wrote that too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-3x-framework&#34;&gt;The 3X Framework&lt;/h2&gt;&#xA;&lt;p&gt;One of the frameworks Kent created while working at Facebook is called &lt;a href=&#34;https://medium.com/@kentbeck_7670/the-product-development-triathlon-6464e2763c46&#34;&gt;3X: Explore, Expand, Extract&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Explore&lt;/strong&gt;: The search for product-market fit. Success requires cheap, rapid experimentation to generate many ideas, tolerating failures, and putting on a &amp;ldquo;hacker&amp;rdquo; hat to minimize the time from question to answer.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Expand&lt;/strong&gt;: The hypergrowth stage. When a successful growth loop is found, focus on scaling up and overcoming bottlenecks. Fast, temporary fixes are valued here to survive the rapid scale.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Extract&lt;/strong&gt;: Maximizing value and ROI. The product matures, and focus shifts to rigorous engineering, optimization, increasing margins, and efficiency.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The industry has been running an Extract playbook for software development for the last 20+ years, and running it fairly well. Agentic coding wipes some that playbook away. People who were very good at running that playbook are having an identity crisis.&lt;/p&gt;&#xA;&lt;p&gt;Beck says we&amp;rsquo;re back in Explore, and we&amp;rsquo;re beginning to write a new playbook. He also makes this great point:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The skill of writing a playbook is completely different than the skill of applying a playbook.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Beck starts applying 3X to agentic engineering in the podcast, but he doesn&amp;rsquo;t go deep. I wanted to think more about it, because I think it explains why this moment feels so disorienting, and why we’re churning through new tools without making any actual forward progress.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reframing-3x-for-a-methodology&#34;&gt;Reframing 3X for a Methodology&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;em&gt;product&lt;/em&gt; 3X, the thing you&amp;rsquo;re searching for is a &lt;strong&gt;growth loop&lt;/strong&gt;: something that compounds with customers. In &lt;em&gt;methodology&lt;/em&gt; 3X, the thing you&amp;rsquo;re searching for is a &lt;strong&gt;transferable practice&lt;/strong&gt;: something that still works when someone other than its inventor does it, on a codebase they didn&amp;rsquo;t write.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Explore is practice discovery.&lt;/strong&gt; Lots of small bets, high tolerance for failure, and observations are legitimate evidence. &amp;ldquo;It worked for me&amp;rdquo; is a real signal. The cost of a failed experiment is small, and we&amp;rsquo;re running a lot of experiments.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Expand is diffusion.&lt;/strong&gt; Taking the discovered practice and scaling it to the rest of the engineering organization. The bottleneck is tribal knowledge — and the deeper problem is that some of it can&amp;rsquo;t be written down at all. The people who invented a practice are exercising judgment they can&amp;rsquo;t fully articulate. So what gets copied is the &lt;em&gt;artifact&lt;/em&gt;, not the judgment behind it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Extract is codification.&lt;/strong&gt; Standards, metrics, compliance, efficiency. The sign of Extract is &lt;em&gt;boring&lt;/em&gt; — Extract&amp;rsquo;s own playbook tells you to choose boring technology. The debates over git, CI, or code review are over. People still write about them, but it’s optimization at the margins, not a rethink of the core practice. When agentic engineering reaches Extract, we&amp;rsquo;ll stop having conferences about it. Maybe.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nobody-agrees-what-phase-were-in&#34;&gt;Nobody Agrees What Phase We&amp;rsquo;re In&lt;/h2&gt;&#xA;&lt;p&gt;What makes our current moment more confusing is the fact that people are not aligned about the phase state. The point of view changes by how far you sit from the actual work.&lt;/p&gt;&#xA;&lt;p&gt;DX ran a panel at their annual conference, and they were direct about where the questions came from: &lt;a href=&#34;https://newsletter.getdx.com/p/ai-productivity-debate&#34;&gt;assumptions circulating in executive teams and boardrooms&lt;/a&gt; that are shaping strategy right now. The panelists — engineering and research leaders from Etsy, Twilio, GitHub, Google, and Microsoft — were read a series of statements and asked to react.&lt;/p&gt;&#xA;&lt;p&gt;Here are the statements:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;An AI-first SDLC means fewer engineers.&lt;/li&gt;&#xA;&lt;li&gt;In five years, more than 50% of code will be written by AI.&lt;/li&gt;&#xA;&lt;li&gt;Leaders need to mandate AI usage to make sure adoption is moving along.&lt;/li&gt;&#xA;&lt;li&gt;The future of software engineering is managing agents rather than writing code.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Headcount. Volume metrics. Mandates. Role redefinition. Every one of those is an &lt;strong&gt;Extract-phase question&lt;/strong&gt;. Not one of them asks what works, or how to spread it. They all assume that&amp;rsquo;s settled and ask how to squeeze value out of it.&lt;/p&gt;&#xA;&lt;p&gt;You can read what the practitioners said back:&lt;/p&gt;&#xA;&lt;p&gt;Eirini Kalliamvakou couldn&amp;rsquo;t answer the &amp;ldquo;managing agents&amp;rdquo; statement without rewriting it first. The real work, she said, is defining intent, setting constraints and guardrails, providing context, and verifying output: engineering at a different level of abstraction. That is a list of Expand-phase work.&lt;/p&gt;&#xA;&lt;p&gt;Rafe Colburn on mandates: the push usually comes from outside engineering, from people afraid of falling behind, and it produces shallow adoption. Brian Houck, who is running a study of around 600 engineers and managers, found their single biggest disagreement is that most engineering &lt;em&gt;managers&lt;/em&gt; think AI usage is a reasonable individual performance metric — and engineers don&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;The practice itself is still wide open. We don&amp;rsquo;t have settled answers on context management, spec-first versus code-first prompting, subagent decomposition, when to let an agent run versus when to interrupt it, or which model to use for which task. We don&amp;rsquo;t even have a settled answer on how to write a &lt;em&gt;prompt&lt;/em&gt;: one study found that &lt;a href=&#34;https://arxiv.org/abs/2302.00438&#34;&gt;semantically equivalent rewrites produced different code 46% of the time, and changed correctness in 28%&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We don&amp;rsquo;t have best practices yet.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Executives are in Extract.&lt;/strong&gt; Mandates, metrics, headcount, margin.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Practitioners are doing Expand.&lt;/strong&gt; Context and knowledge management, guardrails, agent-assisted review &amp;amp; approval, unattended agentic workflows.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Much of the practice is still Explore.&lt;/strong&gt; Nothing is settled, including how to talk to the thing. We know there is something valuable here, but can&amp;rsquo;t quite realize the full potential.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;The further you are from the code, the later in 3X you think we are.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;This is a structural problem. Phase perception is a function of distance from the work — an executive genuinely cannot see the Explore-phase mess, because all they ever see is the output. Some gap between the C-level and the work streams is normal in any transformation. What&amp;rsquo;s abnormal right now is how &lt;em&gt;wide&lt;/em&gt; the gap has become, because the ground moves every week and the layers are drifting apart faster than they can talk to each other.&lt;/p&gt;&#xA;&lt;h2 id=&#34;executives-rush-to-extract&#34;&gt;Executives Rush to Extract&lt;/h2&gt;&#xA;&lt;p&gt;Sooner or later someone asks the question: &lt;em&gt;Is this working?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s a fair question, but because most organizations haven&amp;rsquo;t set up an evaluation loop or a baseline, it&amp;rsquo;s difficult to answer. When you can&amp;rsquo;t prove a practice is working, you reach for the only instruments you’ve been trained to use: Metrics. Mandates. Capital-letter Frameworks.&lt;/p&gt;&#xA;&lt;p&gt;People start using &amp;ldquo;percentage of code written by AI&amp;rdquo; as a KPI — a very game-able metric. Bill Gates supposedly said: &lt;em&gt;measuring software productivity by lines of code is like measuring progress on an airplane by how much it weighs&lt;/em&gt;. Volume metrics simply inflate the exact code review burden that is already our biggest bottleneck. I don&amp;rsquo;t value solutions implemented with large amounts of code, so why would I be happy with agents that do?&lt;/p&gt;&#xA;&lt;p&gt;Premature Extract isn&amp;rsquo;t a separate mistake, it&amp;rsquo;s a symptom. Twenty years of Extract muscle memory, firing at a problem it can&amp;rsquo;t see clearly from where it&amp;rsquo;s standing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;practitioners-are-stuck-in-explore&#34;&gt;Practitioners Are Stuck in Explore&lt;/h2&gt;&#xA;&lt;p&gt;While executives might be quick to assume agentic engineering is in the Extract phase, we have another problem: Practitioners frequently focus on implementation details, like MCP, Skills, and Harnesses — rather than doing the work to stick with a fundamental concept and move it through Expand.&lt;/p&gt;&#xA;&lt;p&gt;We have strong evidence that agentic code generation, code reviews, and even code approvals can create tangible benefits. We have seen evidence that contextual knowledge is a huge benefit to agents. Instead of leaning into those concepts, we tend to churn through a variety of implementation experiments that try new technical approaches, but don&amp;rsquo;t move the core concept forward.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve written about my own experiences with agentic code generation, and how those outcomes yielded 2× to 7× delivery compression, PR cycle time dropping, and experimentation rates increasing. The next steps have to be pushing those outcomes to create business value, not just engineering value. Sometimes it&amp;rsquo;s more fun to play with the implementation details and new tools.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s what we see teams doing today:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Wiring agents into CI/CD pipelines, IDEs, and ticketing systems. Connecting agent harnesses into your existing SDLC workflow.&lt;/li&gt;&#xA;&lt;li&gt;Rapid scaffolding, loose security boundaries, and a high tolerance for flaky agent behavior. Velocity is the priority: get the agent opening PRs and writing tests autonomously.&lt;/li&gt;&#xA;&lt;li&gt;Aggressive adoption of codebase-wide editing agents, automated PR reviewers, and autonomous bug-fixers, to see how much work can be offloaded.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That list shows a desire to move core foundational aspects of agentic engineering into the Expand-phase. We should be looking for bottlenecks as we scale more of the practices into production-level work. The way we&amp;rsquo;re going about it feels very loose and experimental.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;We adopted Expand-phase goals while keeping Explore-phase discipline.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The tools outran the practice. I have to remember that people like myself fueled some of the disconnect: &lt;strong&gt;we handed executives the numbers that made them think the work was done.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;A &lt;a href=&#34;https://www.nber.org/papers/w35275&#34;&gt;recent NBER paper&lt;/a&gt; traced AI&amp;rsquo;s gains through the entire delivery pipeline — lines of code, to commits, to pull requests, to repos, to releases — across more than 100,000 GitHub developers. The gains at the top are enormous, and they grow with each generation of tooling: roughly +40% more commits from autocomplete, +140% from interactive agents, +180% from autonomous agents.&lt;/p&gt;&#xA;&lt;p&gt;Then they follow those gains downstream through the SDLC. The effect on software actually &lt;em&gt;released&lt;/em&gt; tops out around &lt;strong&gt;+30%&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;180% going in. 30% coming out.&lt;/p&gt;&#xA;&lt;p&gt;This is true with my own numbers too. Delivery compression, PR cycle time, experimentation rate — all measured at the front of the pipeline. Everything downstream of the merge is barely agentic or automated. The gains pile up at the front of the pipeline and then hit a wall.&lt;/p&gt;&#xA;&lt;h2 id=&#34;explore-is-not-permission-to-be-sloppy&#34;&gt;Explore Is Not Permission to Be Sloppy&lt;/h2&gt;&#xA;&lt;p&gt;You might think that &amp;ldquo;just try stuff&amp;rdquo; means &amp;ldquo;rigor is optional&amp;rdquo;, but it&amp;rsquo;s not.&lt;/p&gt;&#xA;&lt;p&gt;Explore has its own &lt;strong&gt;discipline&lt;/strong&gt;, but it&amp;rsquo;s specific to the phase: Cheap to try. Easy to reverse. Comprehended and actually evaluated.&lt;/p&gt;&#xA;&lt;p&gt;The evidence standard shifts by phase, and this is what people are getting wrong. &amp;ldquo;It worked for me&amp;rdquo; is &lt;em&gt;sufficient&lt;/em&gt; in Explore. It&amp;rsquo;s &lt;em&gt;dangerous&lt;/em&gt; in Expand. It&amp;rsquo;s &lt;em&gt;useless&lt;/em&gt; in Extract. Most of the arguing happening right now is people applying one phase&amp;rsquo;s evidence standard to another phase&amp;rsquo;s claim.&lt;/p&gt;&#xA;&lt;p&gt;Explore gives you permission to fail. It does not give you permission to &lt;em&gt;not know whether you failed&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;autonomy-is-earned-not-granted&#34;&gt;Autonomy Is Earned, Not Granted&lt;/h2&gt;&#xA;&lt;p&gt;An agent can only run unattended over work whose correctness can be &lt;em&gt;proven without a human looking&lt;/em&gt;. So the path to autonomy isn&amp;rsquo;t &amp;ldquo;trust agents more.&amp;rdquo; It&amp;rsquo;s &amp;ldquo;make more of our work machine-verifiable.&amp;rdquo; Every gap you close turns a class of work into something an agent can safely own.&lt;/p&gt;&#xA;&lt;p&gt;A phase isn&amp;rsquo;t a decision you make, it&amp;rsquo;s a state you demonstrate: Can this practice be verified without you standing over it? Can someone who didn&amp;rsquo;t invent it run it on a codebase they didn&amp;rsquo;t write?&lt;/p&gt;&#xA;&lt;p&gt;So what does earning it look like? It means taking the core pillars of agentic engineering that we know work — management of context, specifying intent, agent code generation, agent code review/approval — and building the actual infrastructure to scale them safely.&lt;/p&gt;&#xA;&lt;p&gt;Here is what we&amp;rsquo;re doing to move those pillars from loose exploration into disciplined expansion:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; Improve and evolve the context we provide to agents to allow them to perform better. Generated code should be simple, concise, and matching the coding patterns we expect from developers. Code reviews should look for the same issues and checks we expect from our expert developers.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Intent:&lt;/strong&gt; Provide clear and concise intention of the outcome we want from the agent. The sharper the intent, the faster we get the desired outcome. Capture the gist of what&amp;rsquo;s been created to deal with cognitive debt as agents write and review more of the code.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code Generation:&lt;/strong&gt; Move beyond developer-steered sessions and allow the agent to run a subset of tasks unattended. This requires agent-verification of the work. If an agent can&amp;rsquo;t verify the outcome, you can&amp;rsquo;t trust the outcome. You&amp;rsquo;re not scaling code generation effectively.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code Review/Approval:&lt;/strong&gt; We&amp;rsquo;re already using agentic code reviews. Start scoring each change by blast radius, confidence, and risk. High confidence &amp;amp; low-risk merges itself; everything else goes to a human reviewer.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;None of this is a playbook. It’s a set of bets, some of which will look naive in a year. Every one of them is an attempt to earn the next phase rather than trying to skip ahead.&lt;/p&gt;&#xA;&lt;p&gt;We are being asked to pay out a maturity dividend on a practice that hasn&amp;rsquo;t matured yet. The cure isn’t a better executive metric; it’s closing the distance. Leaders who actually use these tools on real work stop misreading the phase, because they can finally see the mess.&lt;/p&gt;&#xA;&lt;p&gt;If we want to write the new playbook instead of just blindly applying an old one, we have to stop treating understanding as a side effect.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Autonomy is earned, not granted.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Next up: Taking those four pillars through Expand; What breaks first in each one, and why &lt;strong&gt;understanding&lt;/strong&gt; turns out to be underneath all of them.&lt;/em&gt;&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Agentic engineering is happening at a breakneck pace, and many are wondering if they are making the &amp;ldquo;right&amp;rdquo; decisions and using the &amp;ldquo;right&amp;rdquo; approaches. I&amp;rsquo;ve been a proponent of not chasing the newest fad, but to instead learn about what&amp;rsquo;s being used across the industry and keeping your options open.&lt;/p&gt;&#xA;&lt;p&gt;I was listening to a recent Pragmatic Engineer &lt;a href=&#34;https://newsletter.pragmaticengineer.com/p/how-kent-beck-shapes-the-software&#34;&gt;podcast&lt;/a&gt;, where Gergely was interviewing Kent Beck. Kent has spent decades helping to shape software development through a variety of methodologies. Toward the end of the podcast, Kent describes our current situation with agentic development: &lt;strong&gt;We don&amp;rsquo;t have a playbook yet.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;We don&amp;rsquo;t have a playbook. But we do have a framework for what it feels like to not have one, and Beck wrote that too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-3x-framework&#34;&gt;The 3X Framework&lt;/h2&gt;&#xA;&lt;p&gt;One of the frameworks Kent created while working at Facebook is called &lt;a href=&#34;https://medium.com/@kentbeck_7670/the-product-development-triathlon-6464e2763c46&#34;&gt;3X: Explore, Expand, Extract&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Explore&lt;/strong&gt;: The search for product-market fit. Success requires cheap, rapid experimentation to generate many ideas, tolerating failures, and putting on a &amp;ldquo;hacker&amp;rdquo; hat to minimize the time from question to answer.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Expand&lt;/strong&gt;: The hypergrowth stage. When a successful growth loop is found, focus on scaling up and overcoming bottlenecks. Fast, temporary fixes are valued here to survive the rapid scale.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Extract&lt;/strong&gt;: Maximizing value and ROI. The product matures, and focus shifts to rigorous engineering, optimization, increasing margins, and efficiency.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The industry has been running an Extract playbook for software development for the last 20+ years, and running it fairly well. Agentic coding wipes some that playbook away. People who were very good at running that playbook are having an identity crisis.&lt;/p&gt;&#xA;&lt;p&gt;Beck says we&amp;rsquo;re back in Explore, and we&amp;rsquo;re beginning to write a new playbook. He also makes this great point:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The skill of writing a playbook is completely different than the skill of applying a playbook.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Beck starts applying 3X to agentic engineering in the podcast, but he doesn&amp;rsquo;t go deep. I wanted to think more about it, because I think it explains why this moment feels so disorienting, and why we’re churning through new tools without making any actual forward progress.&lt;/p&gt;&#xA;&lt;h2 id=&#34;reframing-3x-for-a-methodology&#34;&gt;Reframing 3X for a Methodology&lt;/h2&gt;&#xA;&lt;p&gt;In &lt;em&gt;product&lt;/em&gt; 3X, the thing you&amp;rsquo;re searching for is a &lt;strong&gt;growth loop&lt;/strong&gt;: something that compounds with customers. In &lt;em&gt;methodology&lt;/em&gt; 3X, the thing you&amp;rsquo;re searching for is a &lt;strong&gt;transferable practice&lt;/strong&gt;: something that still works when someone other than its inventor does it, on a codebase they didn&amp;rsquo;t write.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Explore is practice discovery.&lt;/strong&gt; Lots of small bets, high tolerance for failure, and observations are legitimate evidence. &amp;ldquo;It worked for me&amp;rdquo; is a real signal. The cost of a failed experiment is small, and we&amp;rsquo;re running a lot of experiments.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Expand is diffusion.&lt;/strong&gt; Taking the discovered practice and scaling it to the rest of the engineering organization. The bottleneck is tribal knowledge — and the deeper problem is that some of it can&amp;rsquo;t be written down at all. The people who invented a practice are exercising judgment they can&amp;rsquo;t fully articulate. So what gets copied is the &lt;em&gt;artifact&lt;/em&gt;, not the judgment behind it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Extract is codification.&lt;/strong&gt; Standards, metrics, compliance, efficiency. The sign of Extract is &lt;em&gt;boring&lt;/em&gt; — Extract&amp;rsquo;s own playbook tells you to choose boring technology. The debates over git, CI, or code review are over. People still write about them, but it’s optimization at the margins, not a rethink of the core practice. When agentic engineering reaches Extract, we&amp;rsquo;ll stop having conferences about it. Maybe.&lt;/p&gt;&#xA;&lt;h2 id=&#34;nobody-agrees-what-phase-were-in&#34;&gt;Nobody Agrees What Phase We&amp;rsquo;re In&lt;/h2&gt;&#xA;&lt;p&gt;What makes our current moment more confusing is the fact that people are not aligned about the phase state. The point of view changes by how far you sit from the actual work.&lt;/p&gt;&#xA;&lt;p&gt;DX ran a panel at their annual conference, and they were direct about where the questions came from: &lt;a href=&#34;https://newsletter.getdx.com/p/ai-productivity-debate&#34;&gt;assumptions circulating in executive teams and boardrooms&lt;/a&gt; that are shaping strategy right now. The panelists — engineering and research leaders from Etsy, Twilio, GitHub, Google, and Microsoft — were read a series of statements and asked to react.&lt;/p&gt;&#xA;&lt;p&gt;Here are the statements:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;An AI-first SDLC means fewer engineers.&lt;/li&gt;&#xA;&lt;li&gt;In five years, more than 50% of code will be written by AI.&lt;/li&gt;&#xA;&lt;li&gt;Leaders need to mandate AI usage to make sure adoption is moving along.&lt;/li&gt;&#xA;&lt;li&gt;The future of software engineering is managing agents rather than writing code.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Headcount. Volume metrics. Mandates. Role redefinition. Every one of those is an &lt;strong&gt;Extract-phase question&lt;/strong&gt;. Not one of them asks what works, or how to spread it. They all assume that&amp;rsquo;s settled and ask how to squeeze value out of it.&lt;/p&gt;&#xA;&lt;p&gt;You can read what the practitioners said back:&lt;/p&gt;&#xA;&lt;p&gt;Eirini Kalliamvakou couldn&amp;rsquo;t answer the &amp;ldquo;managing agents&amp;rdquo; statement without rewriting it first. The real work, she said, is defining intent, setting constraints and guardrails, providing context, and verifying output: engineering at a different level of abstraction. That is a list of Expand-phase work.&lt;/p&gt;&#xA;&lt;p&gt;Rafe Colburn on mandates: the push usually comes from outside engineering, from people afraid of falling behind, and it produces shallow adoption. Brian Houck, who is running a study of around 600 engineers and managers, found their single biggest disagreement is that most engineering &lt;em&gt;managers&lt;/em&gt; think AI usage is a reasonable individual performance metric — and engineers don&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;The practice itself is still wide open. We don&amp;rsquo;t have settled answers on context management, spec-first versus code-first prompting, subagent decomposition, when to let an agent run versus when to interrupt it, or which model to use for which task. We don&amp;rsquo;t even have a settled answer on how to write a &lt;em&gt;prompt&lt;/em&gt;: one study found that &lt;a href=&#34;https://arxiv.org/abs/2302.00438&#34;&gt;semantically equivalent rewrites produced different code 46% of the time, and changed correctness in 28%&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;We don&amp;rsquo;t have best practices yet.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Executives are in Extract.&lt;/strong&gt; Mandates, metrics, headcount, margin.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Practitioners are doing Expand.&lt;/strong&gt; Context and knowledge management, guardrails, agent-assisted review &amp;amp; approval, unattended agentic workflows.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Much of the practice is still Explore.&lt;/strong&gt; Nothing is settled, including how to talk to the thing. We know there is something valuable here, but can&amp;rsquo;t quite realize the full potential.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;The further you are from the code, the later in 3X you think we are.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;This is a structural problem. Phase perception is a function of distance from the work — an executive genuinely cannot see the Explore-phase mess, because all they ever see is the output. Some gap between the C-level and the work streams is normal in any transformation. What&amp;rsquo;s abnormal right now is how &lt;em&gt;wide&lt;/em&gt; the gap has become, because the ground moves every week and the layers are drifting apart faster than they can talk to each other.&lt;/p&gt;&#xA;&lt;h2 id=&#34;executives-rush-to-extract&#34;&gt;Executives Rush to Extract&lt;/h2&gt;&#xA;&lt;p&gt;Sooner or later someone asks the question: &lt;em&gt;Is this working?&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s a fair question, but because most organizations haven&amp;rsquo;t set up an evaluation loop or a baseline, it&amp;rsquo;s difficult to answer. When you can&amp;rsquo;t prove a practice is working, you reach for the only instruments you’ve been trained to use: Metrics. Mandates. Capital-letter Frameworks.&lt;/p&gt;&#xA;&lt;p&gt;People start using &amp;ldquo;percentage of code written by AI&amp;rdquo; as a KPI — a very game-able metric. Bill Gates supposedly said: &lt;em&gt;measuring software productivity by lines of code is like measuring progress on an airplane by how much it weighs&lt;/em&gt;. Volume metrics simply inflate the exact code review burden that is already our biggest bottleneck. I don&amp;rsquo;t value solutions implemented with large amounts of code, so why would I be happy with agents that do?&lt;/p&gt;&#xA;&lt;p&gt;Premature Extract isn&amp;rsquo;t a separate mistake, it&amp;rsquo;s a symptom. Twenty years of Extract muscle memory, firing at a problem it can&amp;rsquo;t see clearly from where it&amp;rsquo;s standing.&lt;/p&gt;&#xA;&lt;h2 id=&#34;practitioners-are-stuck-in-explore&#34;&gt;Practitioners Are Stuck in Explore&lt;/h2&gt;&#xA;&lt;p&gt;While executives might be quick to assume agentic engineering is in the Extract phase, we have another problem: Practitioners frequently focus on implementation details, like MCP, Skills, and Harnesses — rather than doing the work to stick with a fundamental concept and move it through Expand.&lt;/p&gt;&#xA;&lt;p&gt;We have strong evidence that agentic code generation, code reviews, and even code approvals can create tangible benefits. We have seen evidence that contextual knowledge is a huge benefit to agents. Instead of leaning into those concepts, we tend to churn through a variety of implementation experiments that try new technical approaches, but don&amp;rsquo;t move the core concept forward.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve written about my own experiences with agentic code generation, and how those outcomes yielded 2× to 7× delivery compression, PR cycle time dropping, and experimentation rates increasing. The next steps have to be pushing those outcomes to create business value, not just engineering value. Sometimes it&amp;rsquo;s more fun to play with the implementation details and new tools.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s what we see teams doing today:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Wiring agents into CI/CD pipelines, IDEs, and ticketing systems. Connecting agent harnesses into your existing SDLC workflow.&lt;/li&gt;&#xA;&lt;li&gt;Rapid scaffolding, loose security boundaries, and a high tolerance for flaky agent behavior. Velocity is the priority: get the agent opening PRs and writing tests autonomously.&lt;/li&gt;&#xA;&lt;li&gt;Aggressive adoption of codebase-wide editing agents, automated PR reviewers, and autonomous bug-fixers, to see how much work can be offloaded.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That list shows a desire to move core foundational aspects of agentic engineering into the Expand-phase. We should be looking for bottlenecks as we scale more of the practices into production-level work. The way we&amp;rsquo;re going about it feels very loose and experimental.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;We adopted Expand-phase goals while keeping Explore-phase discipline.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The tools outran the practice. I have to remember that people like myself fueled some of the disconnect: &lt;strong&gt;we handed executives the numbers that made them think the work was done.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;A &lt;a href=&#34;https://www.nber.org/papers/w35275&#34;&gt;recent NBER paper&lt;/a&gt; traced AI&amp;rsquo;s gains through the entire delivery pipeline — lines of code, to commits, to pull requests, to repos, to releases — across more than 100,000 GitHub developers. The gains at the top are enormous, and they grow with each generation of tooling: roughly +40% more commits from autocomplete, +140% from interactive agents, +180% from autonomous agents.&lt;/p&gt;&#xA;&lt;p&gt;Then they follow those gains downstream through the SDLC. The effect on software actually &lt;em&gt;released&lt;/em&gt; tops out around &lt;strong&gt;+30%&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;180% going in. 30% coming out.&lt;/p&gt;&#xA;&lt;p&gt;This is true with my own numbers too. Delivery compression, PR cycle time, experimentation rate — all measured at the front of the pipeline. Everything downstream of the merge is barely agentic or automated. The gains pile up at the front of the pipeline and then hit a wall.&lt;/p&gt;&#xA;&lt;h2 id=&#34;explore-is-not-permission-to-be-sloppy&#34;&gt;Explore Is Not Permission to Be Sloppy&lt;/h2&gt;&#xA;&lt;p&gt;You might think that &amp;ldquo;just try stuff&amp;rdquo; means &amp;ldquo;rigor is optional&amp;rdquo;, but it&amp;rsquo;s not.&lt;/p&gt;&#xA;&lt;p&gt;Explore has its own &lt;strong&gt;discipline&lt;/strong&gt;, but it&amp;rsquo;s specific to the phase: Cheap to try. Easy to reverse. Comprehended and actually evaluated.&lt;/p&gt;&#xA;&lt;p&gt;The evidence standard shifts by phase, and this is what people are getting wrong. &amp;ldquo;It worked for me&amp;rdquo; is &lt;em&gt;sufficient&lt;/em&gt; in Explore. It&amp;rsquo;s &lt;em&gt;dangerous&lt;/em&gt; in Expand. It&amp;rsquo;s &lt;em&gt;useless&lt;/em&gt; in Extract. Most of the arguing happening right now is people applying one phase&amp;rsquo;s evidence standard to another phase&amp;rsquo;s claim.&lt;/p&gt;&#xA;&lt;p&gt;Explore gives you permission to fail. It does not give you permission to &lt;em&gt;not know whether you failed&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;autonomy-is-earned-not-granted&#34;&gt;Autonomy Is Earned, Not Granted&lt;/h2&gt;&#xA;&lt;p&gt;An agent can only run unattended over work whose correctness can be &lt;em&gt;proven without a human looking&lt;/em&gt;. So the path to autonomy isn&amp;rsquo;t &amp;ldquo;trust agents more.&amp;rdquo; It&amp;rsquo;s &amp;ldquo;make more of our work machine-verifiable.&amp;rdquo; Every gap you close turns a class of work into something an agent can safely own.&lt;/p&gt;&#xA;&lt;p&gt;A phase isn&amp;rsquo;t a decision you make, it&amp;rsquo;s a state you demonstrate: Can this practice be verified without you standing over it? Can someone who didn&amp;rsquo;t invent it run it on a codebase they didn&amp;rsquo;t write?&lt;/p&gt;&#xA;&lt;p&gt;So what does earning it look like? It means taking the core pillars of agentic engineering that we know work — management of context, specifying intent, agent code generation, agent code review/approval — and building the actual infrastructure to scale them safely.&lt;/p&gt;&#xA;&lt;p&gt;Here is what we&amp;rsquo;re doing to move those pillars from loose exploration into disciplined expansion:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Context:&lt;/strong&gt; Improve and evolve the context we provide to agents to allow them to perform better. Generated code should be simple, concise, and matching the coding patterns we expect from developers. Code reviews should look for the same issues and checks we expect from our expert developers.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Intent:&lt;/strong&gt; Provide clear and concise intention of the outcome we want from the agent. The sharper the intent, the faster we get the desired outcome. Capture the gist of what&amp;rsquo;s been created to deal with cognitive debt as agents write and review more of the code.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code Generation:&lt;/strong&gt; Move beyond developer-steered sessions and allow the agent to run a subset of tasks unattended. This requires agent-verification of the work. If an agent can&amp;rsquo;t verify the outcome, you can&amp;rsquo;t trust the outcome. You&amp;rsquo;re not scaling code generation effectively.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Code Review/Approval:&lt;/strong&gt; We&amp;rsquo;re already using agentic code reviews. Start scoring each change by blast radius, confidence, and risk. High confidence &amp;amp; low-risk merges itself; everything else goes to a human reviewer.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;None of this is a playbook. It’s a set of bets, some of which will look naive in a year. Every one of them is an attempt to earn the next phase rather than trying to skip ahead.&lt;/p&gt;&#xA;&lt;p&gt;We are being asked to pay out a maturity dividend on a practice that hasn&amp;rsquo;t matured yet. The cure isn’t a better executive metric; it’s closing the distance. Leaders who actually use these tools on real work stop misreading the phase, because they can finally see the mess.&lt;/p&gt;&#xA;&lt;p&gt;If we want to write the new playbook instead of just blindly applying an old one, we have to stop treating understanding as a side effect.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Autonomy is earned, not granted.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;em&gt;Next up: Taking those four pillars through Expand; What breaks first in each one, and why &lt;strong&gt;understanding&lt;/strong&gt; turns out to be underneath all of them.&lt;/em&gt;&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Writing a Field Guide to Agentic Engineering Transformation</title>
				<link>https://starkravingfinkle.org/posts/2026/05/agentic-eng-field-guide/</link>
				<pubDate>Thu, 14 May 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/05/agentic-eng-field-guide/</guid>
				<description>&lt;p&gt;We&amp;rsquo;ve been building a more agentic engineering organization at League over the last ten months. I&amp;rsquo;ve been writing blog posts about different aspects along the way. In April, I felt like we&amp;rsquo;d crossed a threshold.&lt;/p&gt;&#xA;&lt;p&gt;A significant part of the engineering organization was not only using coding agents in daily workflows, the teams themselves had redesigned their operating models. Not all teams were doing things the exact same way, but many of them had made deep changes in their sprints, rituals, and processes.&lt;/p&gt;&#xA;&lt;p&gt;We saw meaningful changes in delivery velocity — a 2× baseline on committed milestones, with high-pattern work like migrations and refactors compressing much further. We saw lots more experimentation, and delivery on those experiments. We saw an appetite to take on work that would have been avoided in the past.&lt;/p&gt;&#xA;&lt;p&gt;It felt like a real success and worth writing about. What we did, what worked, what didn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/guides/agentic-eng-transformation/&#34;&gt;Field Guide to Agentic Engineering Transformation&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;I believe software development is mostly a sociotechnical endeavor, so I focus more on the people than the technology. I believe a successful agentic transformation has everything to do with the people and culture. You&amp;rsquo;ll find a lot of that in the field guide.&lt;/p&gt;&#xA;&lt;p&gt;I wanted to write the field guide to help other companies going through the same type of transformation. A lot of the information written about agentic engineering focuses on the tools, or worse, the hype. I wanted to provide something that was more grounded.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;ve crossed a threshold, but we&amp;rsquo;ve not really finished yet. We have the operational challenges you&amp;rsquo;d expect when engineering gets meaningfully more productive. We&amp;rsquo;re working to improve testing automation, local development friction, code review processes, and deployment automation. We&amp;rsquo;re looking at how we create requirements and designs.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;ll try some things, learn some things, and I&amp;rsquo;ll keep writing about it.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;We&amp;rsquo;ve been building a more agentic engineering organization at League over the last ten months. I&amp;rsquo;ve been writing blog posts about different aspects along the way. In April, I felt like we&amp;rsquo;d crossed a threshold.&lt;/p&gt;&#xA;&lt;p&gt;A significant part of the engineering organization was not only using coding agents in daily workflows, the teams themselves had redesigned their operating models. Not all teams were doing things the exact same way, but many of them had made deep changes in their sprints, rituals, and processes.&lt;/p&gt;&#xA;&lt;p&gt;We saw meaningful changes in delivery velocity — a 2× baseline on committed milestones, with high-pattern work like migrations and refactors compressing much further. We saw lots more experimentation, and delivery on those experiments. We saw an appetite to take on work that would have been avoided in the past.&lt;/p&gt;&#xA;&lt;p&gt;It felt like a real success and worth writing about. What we did, what worked, what didn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/guides/agentic-eng-transformation/&#34;&gt;Field Guide to Agentic Engineering Transformation&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;I believe software development is mostly a sociotechnical endeavor, so I focus more on the people than the technology. I believe a successful agentic transformation has everything to do with the people and culture. You&amp;rsquo;ll find a lot of that in the field guide.&lt;/p&gt;&#xA;&lt;p&gt;I wanted to write the field guide to help other companies going through the same type of transformation. A lot of the information written about agentic engineering focuses on the tools, or worse, the hype. I wanted to provide something that was more grounded.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;ve crossed a threshold, but we&amp;rsquo;ve not really finished yet. We have the operational challenges you&amp;rsquo;d expect when engineering gets meaningfully more productive. We&amp;rsquo;re working to improve testing automation, local development friction, code review processes, and deployment automation. We&amp;rsquo;re looking at how we create requirements and designs.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;ll try some things, learn some things, and I&amp;rsquo;ll keep writing about it.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>The Agentic Coding Payoff: Experimentation Velocity</title>
				<link>https://starkravingfinkle.org/posts/2026/04/coding-agents-experimentation-velocity/</link>
				<pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/04/coding-agents-experimentation-velocity/</guid>
				<description>&lt;p&gt;Most teams measure AI impact on software delivery the same way: &lt;strong&gt;before and after comparisons&lt;/strong&gt; on tasks that were already in the plan. A migration that used to take three weeks now takes two days. A bug that took a week to diagnose gets resolved in an afternoon. These are real wins.&lt;/p&gt;&#xA;&lt;p&gt;But they&amp;rsquo;re measuring the floor, not the ceiling.&lt;/p&gt;&#xA;&lt;p&gt;Speed on planned work is a productivity gain. The ability to attempt unplanned work is a strategic one. A team that ships 20% faster will be faster, sure. A team that runs ten experiments a quarter instead of two will, over time, find things the faster team never looked for. Both matter, but only one of them compounds over time.&lt;/p&gt;&#xA;&lt;p&gt;In my &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/04/coding-agents-dont-replace-platforms/&#34;&gt;last post&lt;/a&gt;, I argued that coding agents don&amp;rsquo;t replace platforms — they make building blocks, guardrails, and durable systems more important. That post was about what agents need to work well. This one is about what becomes possible once they do: a shift from &lt;strong&gt;delivery speed to experimentation velocity&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-experimentation-velocity-looks-like&#34;&gt;What Experimentation Velocity Looks Like&lt;/h2&gt;&#xA;&lt;p&gt;When the cost of trying something drops far enough, people try things they never would have planned.&lt;/p&gt;&#xA;&lt;p&gt;Our engineering teams have been running structured agentic hackathons and sprints — short, intense blocks where engineers work on projects, with AI agents as full execution partners. What comes out of those sessions isn&amp;rsquo;t only incremental improvement on planned work. It&amp;rsquo;s entirely new bets that only exist because the cost of placing them collapsed.&lt;/p&gt;&#xA;&lt;p&gt;Some of the work included:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;POC for managing human &amp;amp; agent interactions: Unblocked the architecture decision in days instead of weeks.&lt;/li&gt;&#xA;&lt;li&gt;Frontend analytic event migration project: Turned a discovery task into a full delivery.&lt;/li&gt;&#xA;&lt;li&gt;Backend service: 2-month schedule pull-forward on a foundational platform service.&lt;/li&gt;&#xA;&lt;li&gt;Product team: Effectively delivered one full quarter of roadmap ahead of schedule.&lt;/li&gt;&#xA;&lt;li&gt;Another product team: 6-week schedule pull-forward on a major feature, at reduced capacity.&lt;/li&gt;&#xA;&lt;li&gt;Yet another product team: A backlog item that had no path to delivery got shipped because AI created spare capacity.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These weren&amp;rsquo;t just shortcuts on existing work. They were bets no one would have made when each one cost weeks of effort.&lt;/p&gt;&#xA;&lt;p&gt;Once we started looking for this pattern, we saw it happening more and more. Not because we gave people more time, but because each project went from a multi-week investment to an afternoon experiment. Discovery phases started producing working artifacts instead of just documents: working API scaffolding, configuration management tooling, reusable implementation foundations. The validation process itself became the starting point for building.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-bigger-opportunity-product-and-growth-experimentation&#34;&gt;The Bigger Opportunity: Product and Growth Experimentation&lt;/h2&gt;&#xA;&lt;p&gt;The engineering examples are compelling, but they&amp;rsquo;re not the most interesting part of this shift.&lt;/p&gt;&#xA;&lt;p&gt;The real unlock is what happens when product and growth teams start thinking in terms of experimentation velocity. When the question changes from &amp;ldquo;can we get this on the roadmap?&amp;rdquo; to &amp;ldquo;can we just try it?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;One unexpected outcome from our sprints was a cross-team POC that shipped a genuinely new product interaction model — a way for users to interact with AI directly within the screens they&amp;rsquo;re already using, without switching to a separate chat interface. Context-aware, embedded in the existing product, surfacing alongside whatever the user is already doing. The team built it in a single sprint using the existing backend architecture. It wasn&amp;rsquo;t on any roadmap. A few people had the idea, the cost of trying it was low enough to just go, and within days they had a working demo on two platforms. Within a week, that demo had reshaped how the product team was thinking about AI surface area and they&amp;rsquo;re now using that experiment as the foundation for new product plans. What started as an unplanned two-day bet is actively shaping the roadmap.&lt;/p&gt;&#xA;&lt;p&gt;That sequence (have an idea, validate it quickly, commit if it works) is what high experimentation velocity actually looks like. It&amp;rsquo;s the sequence that matters most for product and growth teams, because it changes the economics of exploration.&lt;/p&gt;&#xA;&lt;p&gt;Think about what product teams spend most of their time on: building cases for what to try next. Writing specs. Prioritizing backlogs. Negotiating for engineering capacity. Most of that overhead exists because trying things is expensive, so you need to be very sure before you commit resources. When trying things becomes cheap, the entire prioritization calculus changes. You don&amp;rsquo;t need a business case to place a small bet. You need a hypothesis and a day.&lt;/p&gt;&#xA;&lt;p&gt;This is the difference between a product team that ships its roadmap 20% faster and one that tests three ideas for every one it used to test. The first team gets efficiency. The second team gets compounding discovery and some of those discoveries become the roadmap.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-new-bottleneck&#34;&gt;The New Bottleneck&lt;/h2&gt;&#xA;&lt;p&gt;Experimentation velocity is only valuable if your organization can act on what experiments reveal.&lt;/p&gt;&#xA;&lt;p&gt;Once engineering capacity stops being the constraint, a new set of bottlenecks becomes visible and they don&amp;rsquo;t live in engineering.&lt;/p&gt;&#xA;&lt;p&gt;Requirements that aren&amp;rsquo;t ready to act on. Design work that can&amp;rsquo;t keep pace with implementation. Product decisions that take longer to make than the feature takes to build. These are now the rate limiters. The constraint has moved from &amp;ldquo;can we build this?&amp;rdquo; to &amp;ldquo;do we know what to build, and can we decide fast enough to keep up with what we&amp;rsquo;re capable of executing?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;The teams pulling the most value from agentic workflows are the ones that treated this as a whole-system change, not just an engineering upgrade. They restructured how requirements get written: specific, testable, agent-ready. They compressed design cycles to match implementation speed. They pushed decision-making closer to the work, with explicit permission to move at 70% confidence and course-correct on real output rather than waiting for perfect plans.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-to-ask-your-teams&#34;&gt;What To Ask Your Teams&lt;/h2&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re evaluating your AI investment primarily through a delivery lens (features shipped, velocity improved, cycle time reduced) you&amp;rsquo;re getting value, but you&amp;rsquo;re probably underestimating what&amp;rsquo;s available.&lt;/p&gt;&#xA;&lt;p&gt;The better question is a pair: &lt;strong&gt;how much faster are we executing what we planned, and what did we try this quarter that we couldn&amp;rsquo;t have tried six months ago?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;If the answer to the second question is a long list of experiments, prototypes, and bets that came out of nowhere, you&amp;rsquo;re seeing the real return. If the answer is just &amp;ldquo;we shipped the roadmap faster,&amp;rdquo; you&amp;rsquo;re leaving the more interesting half on the table.&lt;/p&gt;&#xA;&lt;p&gt;Delivery speed is a good start. Experimentation velocity is the level up. The constraint has moved. The teams winning with AI aren&amp;rsquo;t just faster, they&amp;rsquo;re attempting more. That&amp;rsquo;s the metric worth tracking.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Most teams measure AI impact on software delivery the same way: &lt;strong&gt;before and after comparisons&lt;/strong&gt; on tasks that were already in the plan. A migration that used to take three weeks now takes two days. A bug that took a week to diagnose gets resolved in an afternoon. These are real wins.&lt;/p&gt;&#xA;&lt;p&gt;But they&amp;rsquo;re measuring the floor, not the ceiling.&lt;/p&gt;&#xA;&lt;p&gt;Speed on planned work is a productivity gain. The ability to attempt unplanned work is a strategic one. A team that ships 20% faster will be faster, sure. A team that runs ten experiments a quarter instead of two will, over time, find things the faster team never looked for. Both matter, but only one of them compounds over time.&lt;/p&gt;&#xA;&lt;p&gt;In my &lt;a href=&#34;https://starkravingfinkle.org/posts/2026/04/coding-agents-dont-replace-platforms/&#34;&gt;last post&lt;/a&gt;, I argued that coding agents don&amp;rsquo;t replace platforms — they make building blocks, guardrails, and durable systems more important. That post was about what agents need to work well. This one is about what becomes possible once they do: a shift from &lt;strong&gt;delivery speed to experimentation velocity&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-experimentation-velocity-looks-like&#34;&gt;What Experimentation Velocity Looks Like&lt;/h2&gt;&#xA;&lt;p&gt;When the cost of trying something drops far enough, people try things they never would have planned.&lt;/p&gt;&#xA;&lt;p&gt;Our engineering teams have been running structured agentic hackathons and sprints — short, intense blocks where engineers work on projects, with AI agents as full execution partners. What comes out of those sessions isn&amp;rsquo;t only incremental improvement on planned work. It&amp;rsquo;s entirely new bets that only exist because the cost of placing them collapsed.&lt;/p&gt;&#xA;&lt;p&gt;Some of the work included:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;POC for managing human &amp;amp; agent interactions: Unblocked the architecture decision in days instead of weeks.&lt;/li&gt;&#xA;&lt;li&gt;Frontend analytic event migration project: Turned a discovery task into a full delivery.&lt;/li&gt;&#xA;&lt;li&gt;Backend service: 2-month schedule pull-forward on a foundational platform service.&lt;/li&gt;&#xA;&lt;li&gt;Product team: Effectively delivered one full quarter of roadmap ahead of schedule.&lt;/li&gt;&#xA;&lt;li&gt;Another product team: 6-week schedule pull-forward on a major feature, at reduced capacity.&lt;/li&gt;&#xA;&lt;li&gt;Yet another product team: A backlog item that had no path to delivery got shipped because AI created spare capacity.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These weren&amp;rsquo;t just shortcuts on existing work. They were bets no one would have made when each one cost weeks of effort.&lt;/p&gt;&#xA;&lt;p&gt;Once we started looking for this pattern, we saw it happening more and more. Not because we gave people more time, but because each project went from a multi-week investment to an afternoon experiment. Discovery phases started producing working artifacts instead of just documents: working API scaffolding, configuration management tooling, reusable implementation foundations. The validation process itself became the starting point for building.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-bigger-opportunity-product-and-growth-experimentation&#34;&gt;The Bigger Opportunity: Product and Growth Experimentation&lt;/h2&gt;&#xA;&lt;p&gt;The engineering examples are compelling, but they&amp;rsquo;re not the most interesting part of this shift.&lt;/p&gt;&#xA;&lt;p&gt;The real unlock is what happens when product and growth teams start thinking in terms of experimentation velocity. When the question changes from &amp;ldquo;can we get this on the roadmap?&amp;rdquo; to &amp;ldquo;can we just try it?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;One unexpected outcome from our sprints was a cross-team POC that shipped a genuinely new product interaction model — a way for users to interact with AI directly within the screens they&amp;rsquo;re already using, without switching to a separate chat interface. Context-aware, embedded in the existing product, surfacing alongside whatever the user is already doing. The team built it in a single sprint using the existing backend architecture. It wasn&amp;rsquo;t on any roadmap. A few people had the idea, the cost of trying it was low enough to just go, and within days they had a working demo on two platforms. Within a week, that demo had reshaped how the product team was thinking about AI surface area and they&amp;rsquo;re now using that experiment as the foundation for new product plans. What started as an unplanned two-day bet is actively shaping the roadmap.&lt;/p&gt;&#xA;&lt;p&gt;That sequence (have an idea, validate it quickly, commit if it works) is what high experimentation velocity actually looks like. It&amp;rsquo;s the sequence that matters most for product and growth teams, because it changes the economics of exploration.&lt;/p&gt;&#xA;&lt;p&gt;Think about what product teams spend most of their time on: building cases for what to try next. Writing specs. Prioritizing backlogs. Negotiating for engineering capacity. Most of that overhead exists because trying things is expensive, so you need to be very sure before you commit resources. When trying things becomes cheap, the entire prioritization calculus changes. You don&amp;rsquo;t need a business case to place a small bet. You need a hypothesis and a day.&lt;/p&gt;&#xA;&lt;p&gt;This is the difference between a product team that ships its roadmap 20% faster and one that tests three ideas for every one it used to test. The first team gets efficiency. The second team gets compounding discovery and some of those discoveries become the roadmap.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-new-bottleneck&#34;&gt;The New Bottleneck&lt;/h2&gt;&#xA;&lt;p&gt;Experimentation velocity is only valuable if your organization can act on what experiments reveal.&lt;/p&gt;&#xA;&lt;p&gt;Once engineering capacity stops being the constraint, a new set of bottlenecks becomes visible and they don&amp;rsquo;t live in engineering.&lt;/p&gt;&#xA;&lt;p&gt;Requirements that aren&amp;rsquo;t ready to act on. Design work that can&amp;rsquo;t keep pace with implementation. Product decisions that take longer to make than the feature takes to build. These are now the rate limiters. The constraint has moved from &amp;ldquo;can we build this?&amp;rdquo; to &amp;ldquo;do we know what to build, and can we decide fast enough to keep up with what we&amp;rsquo;re capable of executing?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;The teams pulling the most value from agentic workflows are the ones that treated this as a whole-system change, not just an engineering upgrade. They restructured how requirements get written: specific, testable, agent-ready. They compressed design cycles to match implementation speed. They pushed decision-making closer to the work, with explicit permission to move at 70% confidence and course-correct on real output rather than waiting for perfect plans.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-to-ask-your-teams&#34;&gt;What To Ask Your Teams&lt;/h2&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re evaluating your AI investment primarily through a delivery lens (features shipped, velocity improved, cycle time reduced) you&amp;rsquo;re getting value, but you&amp;rsquo;re probably underestimating what&amp;rsquo;s available.&lt;/p&gt;&#xA;&lt;p&gt;The better question is a pair: &lt;strong&gt;how much faster are we executing what we planned, and what did we try this quarter that we couldn&amp;rsquo;t have tried six months ago?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;If the answer to the second question is a long list of experiments, prototypes, and bets that came out of nowhere, you&amp;rsquo;re seeing the real return. If the answer is just &amp;ldquo;we shipped the roadmap faster,&amp;rdquo; you&amp;rsquo;re leaving the more interesting half on the table.&lt;/p&gt;&#xA;&lt;p&gt;Delivery speed is a good start. Experimentation velocity is the level up. The constraint has moved. The teams winning with AI aren&amp;rsquo;t just faster, they&amp;rsquo;re attempting more. That&amp;rsquo;s the metric worth tracking.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Coding Agents Don&#39;t Replace Platforms — They Make Them More Important</title>
				<link>https://starkravingfinkle.org/posts/2026/04/coding-agents-dont-replace-platforms/</link>
				<pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/04/coding-agents-dont-replace-platforms/</guid>
				<description>&lt;p&gt;There&amp;rsquo;s a growing belief in engineering circles that because coding agents can generate code so cheaply and quickly, we no longer need to invest in platform frameworks, services, and shared systems. The argument goes something like: code is essentially free now, so why bother with reusable building blocks? Just let agents generate what you need on the fly.&lt;/p&gt;&#xA;&lt;p&gt;I think this perspective fundamentally misunderstands both how agents work and what makes software development expensive.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agents-already-use-frameworks&#34;&gt;Agents Already Use Frameworks&lt;/h2&gt;&#xA;&lt;p&gt;Here’s the irony of the “we don’t need platforms anymore” argument: coding agents themselves rely heavily on frameworks, libraries, and established patterns to produce good output.&lt;/p&gt;&#xA;&lt;p&gt;When you ask Claude Code or Cursor to build you a web application, it doesn&amp;rsquo;t reinvent HTTP from scratch. It reaches for React, FastAPI, Express, or whatever framework fits the context. When it writes infrastructure code, it uses Terraform modules or Pulumi components. The agent&amp;rsquo;s effectiveness comes precisely from having well-understood building blocks to compose, not from generating everything from raw primitives.&lt;/p&gt;&#xA;&lt;p&gt;Addy Osmani captured this well in &lt;a href=&#34;https://addyosmani.com/blog/good-spec/&#34;&gt;his piece on writing specs for AI agents&lt;/a&gt;, noting that vague prompts produce vague results while specific constraints and well-defined building blocks dramatically improve output quality.&lt;/p&gt;&#xA;&lt;p&gt;This maps directly to platform engineering. A well-designed internal developer platform is, at its core, a curated set of constraints and building blocks. Golden paths, opinionated templates, standardized observability, security guardrails — these aren&amp;rsquo;t overhead. They&amp;rsquo;re the context that makes both human and AI-driven development faster and more reliable.&lt;/p&gt;&#xA;&lt;h2 id=&#34;constraints-improve-agent-output&#34;&gt;Constraints Improve Agent Output&lt;/h2&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a counterintuitive principle at work here: giving agents &lt;em&gt;fewer&lt;/em&gt; choices often produces &lt;em&gt;better&lt;/em&gt; results.&lt;/p&gt;&#xA;&lt;p&gt;When an agent operates with a blank canvas and unlimited options, it pattern-matches against its training data, which includes code of wildly varying quality. The result is what people call &amp;ldquo;AI slop&amp;rdquo;: technically functional output that&amp;rsquo;s tangled, inconsistent, and painful to maintain. But when you provide architectural constraints, approved component libraries, established patterns, and clear specs, the agent&amp;rsquo;s output improves dramatically.&lt;/p&gt;&#xA;&lt;p&gt;This is why the emerging best practice for agentic development isn&amp;rsquo;t &amp;ldquo;let the agent do whatever it wants&amp;rdquo;. Instead, teams are investing in &lt;code&gt;AGENTS.md&lt;/code&gt; files, architectural constraint validators, spec-driven development workflows, and code health scoring systems that give agents objective signals about quality. As &lt;a href=&#34;https://codescene.com/blog/agentic-ai-coding-best-practice-patterns-for-speed-with-quality&#34;&gt;CodeScene&amp;rsquo;s analysis&lt;/a&gt; of agentic coding patterns concludes: coding via agents requires &lt;em&gt;more&lt;/em&gt; rigor, &lt;em&gt;more&lt;/em&gt; structure, &lt;em&gt;more&lt;/em&gt; code quality — not less.&lt;/p&gt;&#xA;&lt;p&gt;Platform engineering is the organizational expression of exactly this idea. When you give development teams (and their agents) a set of well-maintained building blocks (vetted libraries, standardized service templates, pre-configured CI/CD pipelines, embedded observability) you&amp;rsquo;re constraining the solution space in productive ways. If you need more building blocks, you can use agents to build &lt;em&gt;those&lt;/em&gt; too. The platform grows, but it grows deliberately.&lt;/p&gt;&#xA;&lt;h2 id=&#34;disposable-code-vs-durable-code&#34;&gt;Disposable Code vs Durable Code&lt;/h2&gt;&#xA;&lt;p&gt;Charity Majors wrote a piece that frames this tension well: &lt;a href=&#34;https://www.honeycomb.io/blog/disposable-code-is-here-to-stay&#34;&gt;Disposable Code Is Here to Stay, but Durable Code Is What Runs the World&lt;/a&gt;. Her core argument is that software development is separating into two domains with very different cost models and risk profiles.&lt;/p&gt;&#xA;&lt;p&gt;Disposable code (design mocks, experiments, data processing scripts, prototypes) has exploded since agents made generation cheap. You spin something up, use it, throw it away. Agents are fantastic at this.&lt;/p&gt;&#xA;&lt;p&gt;But then there&amp;rsquo;s the code that runs financial transactions, manages medical records, routes airline flights, and powers commerce sites. This is durable code, and its cost isn&amp;rsquo;t defined by how hard it was to write. The cost is defined by the standards you build to and the entire lifecycle that follows: maintenance, extension, migration, understanding its behavior, and fixing it when it breaks. Disposable code is cheap because you don&amp;rsquo;t even try to maintain it. Durable code is expensive because you must.&lt;/p&gt;&#xA;&lt;p&gt;This distinction matters enormously for the platform engineering conversation. Platform systems (the shared services, frameworks, SDKs, infrastructure components) are definitionally durable code. They&amp;rsquo;re the foundation that many teams and many applications depend on. They need to be reliable, performant, well-tested, observable, and maintained over time. As Majors points out, the cheapest way to build good durable software is by investing in the sociotechnical systems that unlock fast feedback loops: CI/CD, observability, testing. The only companies currently reaping efficiency gains from AI are the ones who already invested in these foundations.&lt;/p&gt;&#xA;&lt;p&gt;Building is the shortest phase of a software system&amp;rsquo;s life. Maintenance is where the real cost lives. A platform of durable building blocks doesn&amp;rsquo;t just speed up initial development — it dramatically reduces the ongoing cost of maintaining everything built on top of it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;specs-code-and-the-feedback-loop&#34;&gt;Specs, Code, and the Feedback Loop&lt;/h2&gt;&#xA;&lt;p&gt;I see enormous value in using agents for durable purposes, not just throwaway scripts. Drew Breunig&amp;rsquo;s experiment with &lt;a href=&#34;https://www.dbreunig.com/2026/01/08/a-software-library-with-no-code.html&#34;&gt;a software library with no code&lt;/a&gt; is an interesting thought experiment. He released &lt;code&gt;whenwords&lt;/code&gt;, a library containing only a spec and tests, no implementation code. You paste the spec into a coding agent, tell it your target language, and it generates a working implementation. It works across Ruby, Python, Rust, Swift, and more.&lt;/p&gt;&#xA;&lt;p&gt;Maybe we don&amp;rsquo;t need to share code at all, just specs and tests. Vercel built &lt;code&gt;just-bash&lt;/code&gt; (a simulated bash environment in TypeScript) and Pydantic built &lt;code&gt;Monty&lt;/code&gt; (a Python interpreter in Rust) using similar approaches — throw a test suite at an agent, make it pass.&lt;/p&gt;&#xA;&lt;p&gt;But in &lt;a href=&#34;https://www.dbreunig.com/2026/03/04/the-spec-driven-development-triangle.html&#34;&gt;a follow-up talk&lt;/a&gt;, Breunig walked back the premise. His biggest takeaway: &amp;ldquo;No-code libraries are toys because they are unproven.&amp;rdquo; The act of implementing code clarifies and communicates intent in ways that specs alone cannot. A spec doesn&amp;rsquo;t truly work until it&amp;rsquo;s implemented, just like software doesn&amp;rsquo;t truly work until it meets the real world.&lt;/p&gt;&#xA;&lt;p&gt;He reframed spec-driven development from a one-way equation (specs + tests + agent = code) into what he calls the &lt;strong&gt;Spec-Driven Development Triangle&lt;/strong&gt;: a feedback loop where implementing code improves the spec, updated specs drive new tests, and the three nodes must be kept in sync as they evolve. Even with well-defined specs, real-world projects generated 20-comment threads debating the &lt;em&gt;right&lt;/em&gt; way to implement something, because no spec is ever complete.&lt;/p&gt;&#xA;&lt;p&gt;Durable platform building blocks aren&amp;rsquo;t just code, they&amp;rsquo;re code that has been through that triangle many times. The spec has been refined by implementation. The tests have been shaped by production behavior. The code has earned trust through time. That feedback loop is precisely what makes platform components more valuable than freshly generated code, no matter how good the agent is.&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a middle ground between &amp;ldquo;generate everything from scratch&amp;rdquo; and &amp;ldquo;only use proven platform code&amp;rdquo; that I find compelling, and it&amp;rsquo;s directly relevant to platform engineering.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-agents-to-extend-platform-reach&#34;&gt;Using Agents to Extend Platform Reach&lt;/h2&gt;&#xA;&lt;p&gt;Consider a server-driven UI system. The real value of such a platform is on the server side: the configuration layout, the integration with data systems for hydration, the logic and filtering for managing delivered content, the APIs for client interaction. This is durable, complex, evolving code that benefits from deep expertise and long-term investment.&lt;/p&gt;&#xA;&lt;p&gt;The frontend SDK, though, is a different story. It&amp;rsquo;s written in a specific tech stack (Swift for iOS, Kotlin for Android, React for web) but the &lt;em&gt;behavior&lt;/em&gt; it implements is largely mechanical: take the server&amp;rsquo;s UI models, render components, handle actions, manage state. If you could extract the specs (the API contracts, the UI model definitions, the action behaviors) you could ask an agent to generate a frontend SDK in a &lt;em&gt;new&lt;/em&gt; tech stack. React Native, Flutter, whatever your team needs next.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t &amp;ldquo;disposable code generated on the fly.&amp;rdquo; It&amp;rsquo;s using agents to build durable code from durable specs, extending the platform&amp;rsquo;s reach to new tech stacks without requiring a team of specialists in each one. The server-side platform remains the source of truth. The generated SDKs are tested against the same contract. The specs are the stable foundation; the implementations are the part that agents can help scale.&lt;/p&gt;&#xA;&lt;p&gt;You could apply the same pattern to client API wrappers. If you have a well-specified API (clear endpoints, typed request/response models, error handling contracts, authentication flows) an agent can generate idiomatic client libraries for Python, Go, TypeScript, Java, and beyond. Not as throwaway code, but as maintained, tested libraries that follow the spec. When the API changes, you update the spec and regenerate.&lt;/p&gt;&#xA;&lt;p&gt;This is something like Breunig&amp;rsquo;s original &amp;ldquo;library with no code&amp;rdquo; concept, but bounded and connected to a larger platform system. The spec isn&amp;rsquo;t floating freely, it&amp;rsquo;s anchored to a real, maintained, durable platform. These generated implementations feed back into the triangle: building the Flutter SDK might reveal ambiguities in the spec that the Swift SDK never surfaced, improving the platform for everyone. The generated code isn&amp;rsquo;t pure commodity. It extends the platform&amp;rsquo;s value into new contexts while strengthening the spec it&amp;rsquo;s built from.&lt;/p&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s recent &lt;a href=&#34;https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/&#34;&gt;A2UI project&lt;/a&gt; hints at a similar future: a declarative UI protocol where agents send component descriptions that different client renderers (web, mobile, desktop) can display natively. The spec is the stable contract; the renderers can be built per-platform, potentially with agent assistance.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-real-relationship-agents-and-platforms-together&#34;&gt;The Real Relationship: Agents and Platforms Together&lt;/h2&gt;&#xA;&lt;p&gt;The emerging picture isn&amp;rsquo;t &lt;em&gt;agents vs. platforms&lt;/em&gt;. It&amp;rsquo;s agents and platforms reinforcing each other.&lt;/p&gt;&#xA;&lt;p&gt;Platforms give agents the constraints, context, and building blocks they need to produce high-quality output. Golden paths become agent-friendly paths. Architectural validators catch agent mistakes before they hit production. Standardized templates mean agents start from a proven foundation rather than a blank page.&lt;/p&gt;&#xA;&lt;p&gt;Agents, in turn, help platforms scale their impact. They can generate implementations from specs across multiple tech stacks. They can help maintain the platform itself, updating templates, running migrations, fixing linting issues across codebases. They can handle the mechanical work of extending platform reach to new contexts.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://platformengineering.org/blog/announcing-the-state-of-platform-engineering-vol-4&#34;&gt;State of Platform Engineering Report&lt;/a&gt; calls this the &amp;ldquo;dual mandate&amp;rdquo; for 2026: platforms must be AI-powered (using agents to enhance developer productivity) and must simultaneously be platforms &lt;em&gt;for&lt;/em&gt; AI (providing the guardrails and governance that agents need to operate safely). Ninety-four percent of organizations now view AI as critical to the future of platform engineering.&lt;/p&gt;&#xA;&lt;p&gt;As the Anthropic &lt;a href=&#34;https://resources.anthropic.com/hubfs/2026%20Agentic%20Coding%20Trends%20Report.pdf&#34;&gt;Agentic Coding Trends Report&lt;/a&gt; puts it, engineers are shifting from writing code to coordinating agents — focusing expertise on architecture, system design, and strategic decisions. That&amp;rsquo;s exactly what platform engineering has always been about: making the right thing the easy thing, so teams can focus on what matters.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-takeaway&#34;&gt;The Takeaway&lt;/h2&gt;&#xA;&lt;p&gt;Code generation getting cheaper doesn&amp;rsquo;t mean platforms get less important. It means they get &lt;em&gt;more&lt;/em&gt; important.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Building blocks improve agent output.&lt;/strong&gt; Agents with constrained, well-defined component libraries produce better code than agents with a blank canvas. Platform engineering provides those blocks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Maintenance outlasts building.&lt;/strong&gt; The cheapest part of a software system&amp;rsquo;s life is writing the first version. Durable building blocks reduce the cumulative maintenance burden across every application built on top of them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Agents can extend platform reach.&lt;/strong&gt; Well-specified platform systems (APIs, UI models, action contracts) can be implemented across new tech stacks with agent assistance, turning platform specs into multi-platform value.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Platforms need to serve agents too.&lt;/strong&gt; As agents become active participants in the development lifecycle, platforms must provide the guardrails, context, and observability that agents need to operate reliably.&lt;/p&gt;&#xA;&lt;p&gt;The organizations that will move fastest aren&amp;rsquo;t the ones letting agents generate everything from scratch. They&amp;rsquo;re the ones giving agents great building blocks to work with and using agents to make those building blocks available everywhere.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;There&amp;rsquo;s a growing belief in engineering circles that because coding agents can generate code so cheaply and quickly, we no longer need to invest in platform frameworks, services, and shared systems. The argument goes something like: code is essentially free now, so why bother with reusable building blocks? Just let agents generate what you need on the fly.&lt;/p&gt;&#xA;&lt;p&gt;I think this perspective fundamentally misunderstands both how agents work and what makes software development expensive.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agents-already-use-frameworks&#34;&gt;Agents Already Use Frameworks&lt;/h2&gt;&#xA;&lt;p&gt;Here’s the irony of the “we don’t need platforms anymore” argument: coding agents themselves rely heavily on frameworks, libraries, and established patterns to produce good output.&lt;/p&gt;&#xA;&lt;p&gt;When you ask Claude Code or Cursor to build you a web application, it doesn&amp;rsquo;t reinvent HTTP from scratch. It reaches for React, FastAPI, Express, or whatever framework fits the context. When it writes infrastructure code, it uses Terraform modules or Pulumi components. The agent&amp;rsquo;s effectiveness comes precisely from having well-understood building blocks to compose, not from generating everything from raw primitives.&lt;/p&gt;&#xA;&lt;p&gt;Addy Osmani captured this well in &lt;a href=&#34;https://addyosmani.com/blog/good-spec/&#34;&gt;his piece on writing specs for AI agents&lt;/a&gt;, noting that vague prompts produce vague results while specific constraints and well-defined building blocks dramatically improve output quality.&lt;/p&gt;&#xA;&lt;p&gt;This maps directly to platform engineering. A well-designed internal developer platform is, at its core, a curated set of constraints and building blocks. Golden paths, opinionated templates, standardized observability, security guardrails — these aren&amp;rsquo;t overhead. They&amp;rsquo;re the context that makes both human and AI-driven development faster and more reliable.&lt;/p&gt;&#xA;&lt;h2 id=&#34;constraints-improve-agent-output&#34;&gt;Constraints Improve Agent Output&lt;/h2&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a counterintuitive principle at work here: giving agents &lt;em&gt;fewer&lt;/em&gt; choices often produces &lt;em&gt;better&lt;/em&gt; results.&lt;/p&gt;&#xA;&lt;p&gt;When an agent operates with a blank canvas and unlimited options, it pattern-matches against its training data, which includes code of wildly varying quality. The result is what people call &amp;ldquo;AI slop&amp;rdquo;: technically functional output that&amp;rsquo;s tangled, inconsistent, and painful to maintain. But when you provide architectural constraints, approved component libraries, established patterns, and clear specs, the agent&amp;rsquo;s output improves dramatically.&lt;/p&gt;&#xA;&lt;p&gt;This is why the emerging best practice for agentic development isn&amp;rsquo;t &amp;ldquo;let the agent do whatever it wants&amp;rdquo;. Instead, teams are investing in &lt;code&gt;AGENTS.md&lt;/code&gt; files, architectural constraint validators, spec-driven development workflows, and code health scoring systems that give agents objective signals about quality. As &lt;a href=&#34;https://codescene.com/blog/agentic-ai-coding-best-practice-patterns-for-speed-with-quality&#34;&gt;CodeScene&amp;rsquo;s analysis&lt;/a&gt; of agentic coding patterns concludes: coding via agents requires &lt;em&gt;more&lt;/em&gt; rigor, &lt;em&gt;more&lt;/em&gt; structure, &lt;em&gt;more&lt;/em&gt; code quality — not less.&lt;/p&gt;&#xA;&lt;p&gt;Platform engineering is the organizational expression of exactly this idea. When you give development teams (and their agents) a set of well-maintained building blocks (vetted libraries, standardized service templates, pre-configured CI/CD pipelines, embedded observability) you&amp;rsquo;re constraining the solution space in productive ways. If you need more building blocks, you can use agents to build &lt;em&gt;those&lt;/em&gt; too. The platform grows, but it grows deliberately.&lt;/p&gt;&#xA;&lt;h2 id=&#34;disposable-code-vs-durable-code&#34;&gt;Disposable Code vs Durable Code&lt;/h2&gt;&#xA;&lt;p&gt;Charity Majors wrote a piece that frames this tension well: &lt;a href=&#34;https://www.honeycomb.io/blog/disposable-code-is-here-to-stay&#34;&gt;Disposable Code Is Here to Stay, but Durable Code Is What Runs the World&lt;/a&gt;. Her core argument is that software development is separating into two domains with very different cost models and risk profiles.&lt;/p&gt;&#xA;&lt;p&gt;Disposable code (design mocks, experiments, data processing scripts, prototypes) has exploded since agents made generation cheap. You spin something up, use it, throw it away. Agents are fantastic at this.&lt;/p&gt;&#xA;&lt;p&gt;But then there&amp;rsquo;s the code that runs financial transactions, manages medical records, routes airline flights, and powers commerce sites. This is durable code, and its cost isn&amp;rsquo;t defined by how hard it was to write. The cost is defined by the standards you build to and the entire lifecycle that follows: maintenance, extension, migration, understanding its behavior, and fixing it when it breaks. Disposable code is cheap because you don&amp;rsquo;t even try to maintain it. Durable code is expensive because you must.&lt;/p&gt;&#xA;&lt;p&gt;This distinction matters enormously for the platform engineering conversation. Platform systems (the shared services, frameworks, SDKs, infrastructure components) are definitionally durable code. They&amp;rsquo;re the foundation that many teams and many applications depend on. They need to be reliable, performant, well-tested, observable, and maintained over time. As Majors points out, the cheapest way to build good durable software is by investing in the sociotechnical systems that unlock fast feedback loops: CI/CD, observability, testing. The only companies currently reaping efficiency gains from AI are the ones who already invested in these foundations.&lt;/p&gt;&#xA;&lt;p&gt;Building is the shortest phase of a software system&amp;rsquo;s life. Maintenance is where the real cost lives. A platform of durable building blocks doesn&amp;rsquo;t just speed up initial development — it dramatically reduces the ongoing cost of maintaining everything built on top of it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;specs-code-and-the-feedback-loop&#34;&gt;Specs, Code, and the Feedback Loop&lt;/h2&gt;&#xA;&lt;p&gt;I see enormous value in using agents for durable purposes, not just throwaway scripts. Drew Breunig&amp;rsquo;s experiment with &lt;a href=&#34;https://www.dbreunig.com/2026/01/08/a-software-library-with-no-code.html&#34;&gt;a software library with no code&lt;/a&gt; is an interesting thought experiment. He released &lt;code&gt;whenwords&lt;/code&gt;, a library containing only a spec and tests, no implementation code. You paste the spec into a coding agent, tell it your target language, and it generates a working implementation. It works across Ruby, Python, Rust, Swift, and more.&lt;/p&gt;&#xA;&lt;p&gt;Maybe we don&amp;rsquo;t need to share code at all, just specs and tests. Vercel built &lt;code&gt;just-bash&lt;/code&gt; (a simulated bash environment in TypeScript) and Pydantic built &lt;code&gt;Monty&lt;/code&gt; (a Python interpreter in Rust) using similar approaches — throw a test suite at an agent, make it pass.&lt;/p&gt;&#xA;&lt;p&gt;But in &lt;a href=&#34;https://www.dbreunig.com/2026/03/04/the-spec-driven-development-triangle.html&#34;&gt;a follow-up talk&lt;/a&gt;, Breunig walked back the premise. His biggest takeaway: &amp;ldquo;No-code libraries are toys because they are unproven.&amp;rdquo; The act of implementing code clarifies and communicates intent in ways that specs alone cannot. A spec doesn&amp;rsquo;t truly work until it&amp;rsquo;s implemented, just like software doesn&amp;rsquo;t truly work until it meets the real world.&lt;/p&gt;&#xA;&lt;p&gt;He reframed spec-driven development from a one-way equation (specs + tests + agent = code) into what he calls the &lt;strong&gt;Spec-Driven Development Triangle&lt;/strong&gt;: a feedback loop where implementing code improves the spec, updated specs drive new tests, and the three nodes must be kept in sync as they evolve. Even with well-defined specs, real-world projects generated 20-comment threads debating the &lt;em&gt;right&lt;/em&gt; way to implement something, because no spec is ever complete.&lt;/p&gt;&#xA;&lt;p&gt;Durable platform building blocks aren&amp;rsquo;t just code, they&amp;rsquo;re code that has been through that triangle many times. The spec has been refined by implementation. The tests have been shaped by production behavior. The code has earned trust through time. That feedback loop is precisely what makes platform components more valuable than freshly generated code, no matter how good the agent is.&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a middle ground between &amp;ldquo;generate everything from scratch&amp;rdquo; and &amp;ldquo;only use proven platform code&amp;rdquo; that I find compelling, and it&amp;rsquo;s directly relevant to platform engineering.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-agents-to-extend-platform-reach&#34;&gt;Using Agents to Extend Platform Reach&lt;/h2&gt;&#xA;&lt;p&gt;Consider a server-driven UI system. The real value of such a platform is on the server side: the configuration layout, the integration with data systems for hydration, the logic and filtering for managing delivered content, the APIs for client interaction. This is durable, complex, evolving code that benefits from deep expertise and long-term investment.&lt;/p&gt;&#xA;&lt;p&gt;The frontend SDK, though, is a different story. It&amp;rsquo;s written in a specific tech stack (Swift for iOS, Kotlin for Android, React for web) but the &lt;em&gt;behavior&lt;/em&gt; it implements is largely mechanical: take the server&amp;rsquo;s UI models, render components, handle actions, manage state. If you could extract the specs (the API contracts, the UI model definitions, the action behaviors) you could ask an agent to generate a frontend SDK in a &lt;em&gt;new&lt;/em&gt; tech stack. React Native, Flutter, whatever your team needs next.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t &amp;ldquo;disposable code generated on the fly.&amp;rdquo; It&amp;rsquo;s using agents to build durable code from durable specs, extending the platform&amp;rsquo;s reach to new tech stacks without requiring a team of specialists in each one. The server-side platform remains the source of truth. The generated SDKs are tested against the same contract. The specs are the stable foundation; the implementations are the part that agents can help scale.&lt;/p&gt;&#xA;&lt;p&gt;You could apply the same pattern to client API wrappers. If you have a well-specified API (clear endpoints, typed request/response models, error handling contracts, authentication flows) an agent can generate idiomatic client libraries for Python, Go, TypeScript, Java, and beyond. Not as throwaway code, but as maintained, tested libraries that follow the spec. When the API changes, you update the spec and regenerate.&lt;/p&gt;&#xA;&lt;p&gt;This is something like Breunig&amp;rsquo;s original &amp;ldquo;library with no code&amp;rdquo; concept, but bounded and connected to a larger platform system. The spec isn&amp;rsquo;t floating freely, it&amp;rsquo;s anchored to a real, maintained, durable platform. These generated implementations feed back into the triangle: building the Flutter SDK might reveal ambiguities in the spec that the Swift SDK never surfaced, improving the platform for everyone. The generated code isn&amp;rsquo;t pure commodity. It extends the platform&amp;rsquo;s value into new contexts while strengthening the spec it&amp;rsquo;s built from.&lt;/p&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s recent &lt;a href=&#34;https://developers.googleblog.com/introducing-a2ui-an-open-project-for-agent-driven-interfaces/&#34;&gt;A2UI project&lt;/a&gt; hints at a similar future: a declarative UI protocol where agents send component descriptions that different client renderers (web, mobile, desktop) can display natively. The spec is the stable contract; the renderers can be built per-platform, potentially with agent assistance.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-real-relationship-agents-and-platforms-together&#34;&gt;The Real Relationship: Agents and Platforms Together&lt;/h2&gt;&#xA;&lt;p&gt;The emerging picture isn&amp;rsquo;t &lt;em&gt;agents vs. platforms&lt;/em&gt;. It&amp;rsquo;s agents and platforms reinforcing each other.&lt;/p&gt;&#xA;&lt;p&gt;Platforms give agents the constraints, context, and building blocks they need to produce high-quality output. Golden paths become agent-friendly paths. Architectural validators catch agent mistakes before they hit production. Standardized templates mean agents start from a proven foundation rather than a blank page.&lt;/p&gt;&#xA;&lt;p&gt;Agents, in turn, help platforms scale their impact. They can generate implementations from specs across multiple tech stacks. They can help maintain the platform itself, updating templates, running migrations, fixing linting issues across codebases. They can handle the mechanical work of extending platform reach to new contexts.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://platformengineering.org/blog/announcing-the-state-of-platform-engineering-vol-4&#34;&gt;State of Platform Engineering Report&lt;/a&gt; calls this the &amp;ldquo;dual mandate&amp;rdquo; for 2026: platforms must be AI-powered (using agents to enhance developer productivity) and must simultaneously be platforms &lt;em&gt;for&lt;/em&gt; AI (providing the guardrails and governance that agents need to operate safely). Ninety-four percent of organizations now view AI as critical to the future of platform engineering.&lt;/p&gt;&#xA;&lt;p&gt;As the Anthropic &lt;a href=&#34;https://resources.anthropic.com/hubfs/2026%20Agentic%20Coding%20Trends%20Report.pdf&#34;&gt;Agentic Coding Trends Report&lt;/a&gt; puts it, engineers are shifting from writing code to coordinating agents — focusing expertise on architecture, system design, and strategic decisions. That&amp;rsquo;s exactly what platform engineering has always been about: making the right thing the easy thing, so teams can focus on what matters.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-takeaway&#34;&gt;The Takeaway&lt;/h2&gt;&#xA;&lt;p&gt;Code generation getting cheaper doesn&amp;rsquo;t mean platforms get less important. It means they get &lt;em&gt;more&lt;/em&gt; important.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Building blocks improve agent output.&lt;/strong&gt; Agents with constrained, well-defined component libraries produce better code than agents with a blank canvas. Platform engineering provides those blocks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Maintenance outlasts building.&lt;/strong&gt; The cheapest part of a software system&amp;rsquo;s life is writing the first version. Durable building blocks reduce the cumulative maintenance burden across every application built on top of them.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Agents can extend platform reach.&lt;/strong&gt; Well-specified platform systems (APIs, UI models, action contracts) can be implemented across new tech stacks with agent assistance, turning platform specs into multi-platform value.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Platforms need to serve agents too.&lt;/strong&gt; As agents become active participants in the development lifecycle, platforms must provide the guardrails, context, and observability that agents need to operate reliably.&lt;/p&gt;&#xA;&lt;p&gt;The organizations that will move fastest aren&amp;rsquo;t the ones letting agents generate everything from scratch. They&amp;rsquo;re the ones giving agents great building blocks to work with and using agents to make those building blocks available everywhere.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Agentic Coding: Who Will Review All That Code?</title>
				<link>https://starkravingfinkle.org/posts/2026/03/coding-agents-who-will-review-all-that-code/</link>
				<pubDate>Sun, 01 Mar 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/03/coding-agents-who-will-review-all-that-code/</guid>
				<description>&lt;p&gt;League (my day job) recently enabled mandatory GitHub Copilot code reviews on all pull requests. Not that long ago, I would not have supported this idea. Models, and the agent systems that use the models, have improved to the point of frequently finding non-trivial issues with code in a pull request.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;d had Copilot and Codex reviews enabled for a while, but they were optional. Easy to ignore. For those who actually read the reviews, we watched the quality get better and better. Good enough to make mandatory and see how it works.&lt;/p&gt;&#xA;&lt;p&gt;As we use coding agents to create more code, it follows that we&amp;rsquo;re going to need to find ways to review all that code. Before I get side-tracked into talking about agentic code reviews, we should probably figure out what parts of code reviews matter the most.&lt;/p&gt;&#xA;&lt;h2 id=&#34;code-reviews-dont-catch-many-bugs&#34;&gt;Code Reviews Don&amp;rsquo;t Catch Many Bugs&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the thing most engineering leaders don&amp;rsquo;t want to accept: &lt;strong&gt;human code reviews are not very good at finding defects.&lt;/strong&gt; We all &lt;em&gt;think&lt;/em&gt; they are. We&amp;rsquo;d &lt;em&gt;like&lt;/em&gt; them to be. The research has been telling us otherwise for over a decade.&lt;/p&gt;&#xA;&lt;p&gt;[this is where I asked Claude to summarize some research for me]&lt;/p&gt;&#xA;&lt;p&gt;The 2013 Microsoft Research study by Bacchelli and Bird — &amp;ldquo;&lt;a href=&#34;https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/ICSE202013-codereview.pdf&#34;&gt;Expectations, Outcomes, and Challenges of Modern Code Review&lt;/a&gt;&amp;rdquo; — analyzed hundreds of review comments, surveyed 873 programmers and 165 managers, and observed developers in the act of reviewing. They found that only &lt;strong&gt;14% of review comments addressed defects.&lt;/strong&gt; The most common category? Code improvements: readability, consistency, dead code removal. A follow-up study by Czerwonka, Greiler, and Tilford at Microsoft (2015), bluntly titled &amp;ldquo;&lt;a href=&#34;https://www.microsoft.com/en-us/research/publication/code-reviews-do-not-find-bugs-how-the-current-code-review-best-practice-slows-us-down/&#34;&gt;Code Reviews Do Not Find Bugs&lt;/a&gt;,&amp;rdquo; corroborated those findings across 25,000 developers: roughly &lt;strong&gt;15% of reviewer comments indicated a possible defect&lt;/strong&gt;, while about half concerned long-term maintainability.&lt;/p&gt;&#xA;&lt;p&gt;And yet, 44% of developers in Bacchelli and Bird&amp;rsquo;s survey ranked &amp;ldquo;finding defects&amp;rdquo; as their number one motivation for doing code reviews. There is a persistent gap between what we believe code reviews do and what they actually do.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://wiki.aalto.fi/spaces/~mmantyla@aalto.fi/pages/58069599/What+Types+of+Defects+Are+Really+Discovered+in+Code+Reviews&#34;&gt;Mäntylä and Lassenius&lt;/a&gt; found that &lt;strong&gt;75% of defects discovered during code review were &amp;ldquo;evolvability&amp;rdquo; defects&lt;/strong&gt; — issues affecting maintainability and comprehensibility — not functional bugs.&lt;/p&gt;&#xA;&lt;p&gt;Most code reviews find no bugs at all. This doesn&amp;rsquo;t mean the bugs don&amp;rsquo;t exist, only that reviewers didn&amp;rsquo;t find them.&lt;/p&gt;&#xA;&lt;p&gt;Someone should point out that &lt;a href=&#34;https://entropicthoughts.com/code-reviews-do-find-bugs&#34;&gt;finding 15% of the defects is better than nothing&lt;/a&gt;, and I&amp;rsquo;d agree. It would be tragic if something happened to cause that detection rate to drop.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-are-code-reviews-good-at&#34;&gt;What Are Code Reviews Good At?&lt;/h2&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s engineering practices documentation (&lt;a href=&#34;https://abseil.io/resources/swe-book/html/ch09.html&#34;&gt;SWE Book&lt;/a&gt;) says: &amp;ldquo;Checking for code correctness is NOT the primary benefit Google accrues from the process of code review.&amp;rdquo; Instead, Google identifies the core benefits as code comprehension, consistency enforcement, knowledge sharing, psychological ownership, and validation, with correctness listed last.&lt;/p&gt;&#xA;&lt;p&gt;A &lt;a href=&#34;https://arxiv.org/pdf/2306.08980&#34;&gt;study of code review networks&lt;/a&gt; at Microsoft, Spotify, and Trivago found that an average participant can spread information to &lt;strong&gt;72–85% of all code review participants within four weeks.&lt;/strong&gt; Information travels between any two participants in fewer than five hops and fewer than five days. Code review isn&amp;rsquo;t just a quality gate. It&amp;rsquo;s the primary nervous system through which engineering organizations maintain shared understanding of their own systems.&lt;/p&gt;&#xA;&lt;p&gt;The real value of code reviews:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Knowledge transfer&lt;/strong&gt;: Someone other than the author learns how the system works. This is how organizations reduce bus factor, onboard new engineers, and maintain the ability to debug systems at 2am.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Architectural oversight&lt;/strong&gt;: Code owners and senior engineers catch changes that work locally but don&amp;rsquo;t fit the broader system design — wrong abstractions, inconsistent patterns, unintended coupling.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Norm maintenance&lt;/strong&gt;: Teams calibrate on naming conventions, error handling strategies, testing approaches, and what &amp;ldquo;good&amp;rdquo; looks like. This is social, not mechanical. Once a decision has been made, it can become mechanical.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Shared awareness&lt;/strong&gt;: Reviews keep the team aware of what&amp;rsquo;s changing and why. This context is invisible until it&amp;rsquo;s missing.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;None of these are things a linter catches. None of these are things you get from a passing test suite. Most critically, these are exactly the things that get lost when AI generates code that no human fully understands. Read what others are writing about the &amp;ldquo;&lt;a href=&#34;https://margaretstorey.com/blog/2026/02/09/cognitive-debt/&#34;&gt;cognitive debt&lt;/a&gt;&amp;rdquo; that happens with coding agents.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agentic-coding-makes-the-problem-worse&#34;&gt;Agentic Coding Makes the Problem Worse&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://www.cortex.io/post/ai-is-making-engineering-faster-but-not-better-state-of-ai-benchmark-2026&#34;&gt;Cortex 2026 Benchmark Report&lt;/a&gt; found PRs per author up 20% year-over-year, but incidents per pull request increased 23.5% and change failure rates rose roughly 30%. The DORA Report documented a 7.2% reduction in delivery stability with heavy AI reliance — even though 75% of developers &lt;em&gt;felt&lt;/em&gt; more productive. AI-assisted PRs are 18% larger on average. Median PR size increased 33% in 2025.&lt;/p&gt;&#xA;&lt;p&gt;So we&amp;rsquo;re producing more code, faster, in larger chunks, with more subtle defects — and asking humans to review it all. This won&amp;rsquo;t scale. Faros AI data across 10,000+ developers shows code review time has increased by roughly 91% as PR volume and size increased. An IT Pro/Qodo survey found that nearly half of developers don&amp;rsquo;t check AI-generated code at all, with 38% saying it takes &lt;em&gt;longer&lt;/em&gt; to review than code from colleagues.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-agents-to-review-agent-written-code&#34;&gt;Using Agents to Review Agent-Written Code&lt;/h2&gt;&#xA;&lt;p&gt;This is why League enabled mandatory Copilot reviews. We&amp;rsquo;re using agents to cover some of the review capacity that human teams can no longer provide at the volume AI-generated code demands. And honestly, agents are &lt;em&gt;pretty good&lt;/em&gt; at the mechanical layer of review. They catch the things that can be tedious for humans — improper framework usage, inconsistent error handling, basic security patterns. The models have gotten good enough at this to be worth mandating.&lt;/p&gt;&#xA;&lt;p&gt;The emerging model is layered review:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Real-time IDE feedback&lt;/strong&gt; — the agent catches issues as you write (or as another agent writes).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Automated PR review&lt;/strong&gt; — Copilot, Codex, Claude, or similar tools handle the mechanical layer at submission time.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Human review for the hard parts&lt;/strong&gt; — architecture, security threat modeling, business logic, and the &amp;ldquo;does this fit our system&amp;rdquo; judgment calls.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The key insight is that automated review should &lt;em&gt;free up&lt;/em&gt; human attention for the things humans are uniquely good at, not replace human review entirely.&lt;/p&gt;&#xA;&lt;h2 id=&#34;focus-more-on-code-planning&#34;&gt;Focus More on Code Planning&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s a spicy take: &lt;strong&gt;we could probably do fewer code reviews if we got better at code planning.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;With coding agents, we need to be very clear and explicit with upfront requirements. We&amp;rsquo;ve even started calling this &amp;ldquo;&lt;a href=&#34;https://www.thoughtworks.com/insights/blog/agile-engineering-practices/spec-driven-development-unpacking-2025-new-engineering-practices&#34;&gt;spec-driven development&lt;/a&gt;.&amp;rdquo; That extra time spent at the beginning of the process, along with guidance given to the agents through rules files, architecture documents, and the tests used to verify results. It could mean the need for code reviews decreases.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t a new idea, but agentic coding makes it more urgent. When a human writes code, they carry implicit context: they know the team&amp;rsquo;s conventions, the system&amp;rsquo;s quirks, the reasons behind past decisions. When an agent writes code, all of that context has to be &lt;em&gt;explicit&lt;/em&gt;. It has to be written down. And the act of writing it down — of specifying what you want, how it should integrate, what patterns to follow, what to avoid — is itself a form of design review that happens &lt;em&gt;before&lt;/em&gt; a single line of code is generated.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://addyo.substack.com/p/code-review-in-the-age-of-ai&#34;&gt;Addy Osmani&amp;rsquo;s &amp;ldquo;PR Contract&amp;rdquo; framework&lt;/a&gt; captures this well. Every PR should declare: what and why in a sentence or two, proof it works via tests or demo, a risk tier and which parts were AI-generated, and one or two specific areas requiring human review focus. The core principle: if you can&amp;rsquo;t explain why the code works, you shouldn&amp;rsquo;t be asking someone else to approve it.&lt;/p&gt;&#xA;&lt;p&gt;The organizations adapting best are investing heavily in what happens upstream of the pull request:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Architecture decision records and design docs&lt;/strong&gt; that agents and reviewers can reference&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Project rules files&lt;/strong&gt; (&lt;code&gt;.cursor/rules&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;copilot-instructions.md&lt;/code&gt;) that encode team conventions so agents generate code that fits the system from the start&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Spec-driven development&lt;/strong&gt; where the specification is reviewed and approved before any code is generated&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Test-first workflows&lt;/strong&gt; where agents must execute and pass tests before submitting for review&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;When you invest in the inputs, you reduce the burden on the review. The review becomes a confirmation that the plan was followed, not a discovery process for whether the plan was any good.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-this-is-heading&#34;&gt;Where This Is Heading&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;m not saying we should stop reviewing code today, but I do think we&amp;rsquo;ll stop reviewing as much code in a year.&lt;/p&gt;&#xA;&lt;p&gt;The answer, I think, involves three shifts: let agents handle the mechanical review they&amp;rsquo;re now good enough at, invest heavily in what happens before code is written (specs, architecture, rules), and be intentional about preserving the knowledge-sharing function of review even if that means reviewing &lt;em&gt;less&lt;/em&gt; code, more &lt;em&gt;carefully&lt;/em&gt;, with a focus on system-level understanding rather than line-by-line correctness.&lt;/p&gt;&#xA;&lt;p&gt;The risk is knowledge erosion and cognitive debt. If humans stop reading code because agents write it and other agents review it, the team loses the shared understanding that code review has always quietly maintained. The Opsera 2026 benchmark found that senior engineers realize nearly &lt;a href=&#34;https://www.cjroth.com/blog/2026-02-18-building-an-elite-engineering-culture&#34;&gt;five times the productivity gains of junior engineers&lt;/a&gt; from AI tools — precisely because they already possess the system-level understanding needed to evaluate AI output. AI amplifies existing expertise. It doesn&amp;rsquo;t create it.&lt;/p&gt;&#xA;&lt;p&gt;So the question isn&amp;rsquo;t really &amp;ldquo;who will review all that code?&amp;rdquo; The question is: &lt;strong&gt;how do we maintain the knowledge transfer, architectural oversight, and shared understanding that code reviews have always provided — when the volume of code outstrips our capacity to read it all?&lt;/strong&gt;&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;League (my day job) recently enabled mandatory GitHub Copilot code reviews on all pull requests. Not that long ago, I would not have supported this idea. Models, and the agent systems that use the models, have improved to the point of frequently finding non-trivial issues with code in a pull request.&lt;/p&gt;&#xA;&lt;p&gt;We&amp;rsquo;d had Copilot and Codex reviews enabled for a while, but they were optional. Easy to ignore. For those who actually read the reviews, we watched the quality get better and better. Good enough to make mandatory and see how it works.&lt;/p&gt;&#xA;&lt;p&gt;As we use coding agents to create more code, it follows that we&amp;rsquo;re going to need to find ways to review all that code. Before I get side-tracked into talking about agentic code reviews, we should probably figure out what parts of code reviews matter the most.&lt;/p&gt;&#xA;&lt;h2 id=&#34;code-reviews-dont-catch-many-bugs&#34;&gt;Code Reviews Don&amp;rsquo;t Catch Many Bugs&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the thing most engineering leaders don&amp;rsquo;t want to accept: &lt;strong&gt;human code reviews are not very good at finding defects.&lt;/strong&gt; We all &lt;em&gt;think&lt;/em&gt; they are. We&amp;rsquo;d &lt;em&gt;like&lt;/em&gt; them to be. The research has been telling us otherwise for over a decade.&lt;/p&gt;&#xA;&lt;p&gt;[this is where I asked Claude to summarize some research for me]&lt;/p&gt;&#xA;&lt;p&gt;The 2013 Microsoft Research study by Bacchelli and Bird — &amp;ldquo;&lt;a href=&#34;https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/ICSE202013-codereview.pdf&#34;&gt;Expectations, Outcomes, and Challenges of Modern Code Review&lt;/a&gt;&amp;rdquo; — analyzed hundreds of review comments, surveyed 873 programmers and 165 managers, and observed developers in the act of reviewing. They found that only &lt;strong&gt;14% of review comments addressed defects.&lt;/strong&gt; The most common category? Code improvements: readability, consistency, dead code removal. A follow-up study by Czerwonka, Greiler, and Tilford at Microsoft (2015), bluntly titled &amp;ldquo;&lt;a href=&#34;https://www.microsoft.com/en-us/research/publication/code-reviews-do-not-find-bugs-how-the-current-code-review-best-practice-slows-us-down/&#34;&gt;Code Reviews Do Not Find Bugs&lt;/a&gt;,&amp;rdquo; corroborated those findings across 25,000 developers: roughly &lt;strong&gt;15% of reviewer comments indicated a possible defect&lt;/strong&gt;, while about half concerned long-term maintainability.&lt;/p&gt;&#xA;&lt;p&gt;And yet, 44% of developers in Bacchelli and Bird&amp;rsquo;s survey ranked &amp;ldquo;finding defects&amp;rdquo; as their number one motivation for doing code reviews. There is a persistent gap between what we believe code reviews do and what they actually do.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://wiki.aalto.fi/spaces/~mmantyla@aalto.fi/pages/58069599/What+Types+of+Defects+Are+Really+Discovered+in+Code+Reviews&#34;&gt;Mäntylä and Lassenius&lt;/a&gt; found that &lt;strong&gt;75% of defects discovered during code review were &amp;ldquo;evolvability&amp;rdquo; defects&lt;/strong&gt; — issues affecting maintainability and comprehensibility — not functional bugs.&lt;/p&gt;&#xA;&lt;p&gt;Most code reviews find no bugs at all. This doesn&amp;rsquo;t mean the bugs don&amp;rsquo;t exist, only that reviewers didn&amp;rsquo;t find them.&lt;/p&gt;&#xA;&lt;p&gt;Someone should point out that &lt;a href=&#34;https://entropicthoughts.com/code-reviews-do-find-bugs&#34;&gt;finding 15% of the defects is better than nothing&lt;/a&gt;, and I&amp;rsquo;d agree. It would be tragic if something happened to cause that detection rate to drop.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-are-code-reviews-good-at&#34;&gt;What Are Code Reviews Good At?&lt;/h2&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s engineering practices documentation (&lt;a href=&#34;https://abseil.io/resources/swe-book/html/ch09.html&#34;&gt;SWE Book&lt;/a&gt;) says: &amp;ldquo;Checking for code correctness is NOT the primary benefit Google accrues from the process of code review.&amp;rdquo; Instead, Google identifies the core benefits as code comprehension, consistency enforcement, knowledge sharing, psychological ownership, and validation, with correctness listed last.&lt;/p&gt;&#xA;&lt;p&gt;A &lt;a href=&#34;https://arxiv.org/pdf/2306.08980&#34;&gt;study of code review networks&lt;/a&gt; at Microsoft, Spotify, and Trivago found that an average participant can spread information to &lt;strong&gt;72–85% of all code review participants within four weeks.&lt;/strong&gt; Information travels between any two participants in fewer than five hops and fewer than five days. Code review isn&amp;rsquo;t just a quality gate. It&amp;rsquo;s the primary nervous system through which engineering organizations maintain shared understanding of their own systems.&lt;/p&gt;&#xA;&lt;p&gt;The real value of code reviews:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Knowledge transfer&lt;/strong&gt;: Someone other than the author learns how the system works. This is how organizations reduce bus factor, onboard new engineers, and maintain the ability to debug systems at 2am.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Architectural oversight&lt;/strong&gt;: Code owners and senior engineers catch changes that work locally but don&amp;rsquo;t fit the broader system design — wrong abstractions, inconsistent patterns, unintended coupling.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Norm maintenance&lt;/strong&gt;: Teams calibrate on naming conventions, error handling strategies, testing approaches, and what &amp;ldquo;good&amp;rdquo; looks like. This is social, not mechanical. Once a decision has been made, it can become mechanical.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Shared awareness&lt;/strong&gt;: Reviews keep the team aware of what&amp;rsquo;s changing and why. This context is invisible until it&amp;rsquo;s missing.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;None of these are things a linter catches. None of these are things you get from a passing test suite. Most critically, these are exactly the things that get lost when AI generates code that no human fully understands. Read what others are writing about the &amp;ldquo;&lt;a href=&#34;https://margaretstorey.com/blog/2026/02/09/cognitive-debt/&#34;&gt;cognitive debt&lt;/a&gt;&amp;rdquo; that happens with coding agents.&lt;/p&gt;&#xA;&lt;h2 id=&#34;agentic-coding-makes-the-problem-worse&#34;&gt;Agentic Coding Makes the Problem Worse&lt;/h2&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://www.cortex.io/post/ai-is-making-engineering-faster-but-not-better-state-of-ai-benchmark-2026&#34;&gt;Cortex 2026 Benchmark Report&lt;/a&gt; found PRs per author up 20% year-over-year, but incidents per pull request increased 23.5% and change failure rates rose roughly 30%. The DORA Report documented a 7.2% reduction in delivery stability with heavy AI reliance — even though 75% of developers &lt;em&gt;felt&lt;/em&gt; more productive. AI-assisted PRs are 18% larger on average. Median PR size increased 33% in 2025.&lt;/p&gt;&#xA;&lt;p&gt;So we&amp;rsquo;re producing more code, faster, in larger chunks, with more subtle defects — and asking humans to review it all. This won&amp;rsquo;t scale. Faros AI data across 10,000+ developers shows code review time has increased by roughly 91% as PR volume and size increased. An IT Pro/Qodo survey found that nearly half of developers don&amp;rsquo;t check AI-generated code at all, with 38% saying it takes &lt;em&gt;longer&lt;/em&gt; to review than code from colleagues.&lt;/p&gt;&#xA;&lt;h2 id=&#34;using-agents-to-review-agent-written-code&#34;&gt;Using Agents to Review Agent-Written Code&lt;/h2&gt;&#xA;&lt;p&gt;This is why League enabled mandatory Copilot reviews. We&amp;rsquo;re using agents to cover some of the review capacity that human teams can no longer provide at the volume AI-generated code demands. And honestly, agents are &lt;em&gt;pretty good&lt;/em&gt; at the mechanical layer of review. They catch the things that can be tedious for humans — improper framework usage, inconsistent error handling, basic security patterns. The models have gotten good enough at this to be worth mandating.&lt;/p&gt;&#xA;&lt;p&gt;The emerging model is layered review:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Real-time IDE feedback&lt;/strong&gt; — the agent catches issues as you write (or as another agent writes).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Automated PR review&lt;/strong&gt; — Copilot, Codex, Claude, or similar tools handle the mechanical layer at submission time.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Human review for the hard parts&lt;/strong&gt; — architecture, security threat modeling, business logic, and the &amp;ldquo;does this fit our system&amp;rdquo; judgment calls.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;The key insight is that automated review should &lt;em&gt;free up&lt;/em&gt; human attention for the things humans are uniquely good at, not replace human review entirely.&lt;/p&gt;&#xA;&lt;h2 id=&#34;focus-more-on-code-planning&#34;&gt;Focus More on Code Planning&lt;/h2&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s a spicy take: &lt;strong&gt;we could probably do fewer code reviews if we got better at code planning.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;With coding agents, we need to be very clear and explicit with upfront requirements. We&amp;rsquo;ve even started calling this &amp;ldquo;&lt;a href=&#34;https://www.thoughtworks.com/insights/blog/agile-engineering-practices/spec-driven-development-unpacking-2025-new-engineering-practices&#34;&gt;spec-driven development&lt;/a&gt;.&amp;rdquo; That extra time spent at the beginning of the process, along with guidance given to the agents through rules files, architecture documents, and the tests used to verify results. It could mean the need for code reviews decreases.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t a new idea, but agentic coding makes it more urgent. When a human writes code, they carry implicit context: they know the team&amp;rsquo;s conventions, the system&amp;rsquo;s quirks, the reasons behind past decisions. When an agent writes code, all of that context has to be &lt;em&gt;explicit&lt;/em&gt;. It has to be written down. And the act of writing it down — of specifying what you want, how it should integrate, what patterns to follow, what to avoid — is itself a form of design review that happens &lt;em&gt;before&lt;/em&gt; a single line of code is generated.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://addyo.substack.com/p/code-review-in-the-age-of-ai&#34;&gt;Addy Osmani&amp;rsquo;s &amp;ldquo;PR Contract&amp;rdquo; framework&lt;/a&gt; captures this well. Every PR should declare: what and why in a sentence or two, proof it works via tests or demo, a risk tier and which parts were AI-generated, and one or two specific areas requiring human review focus. The core principle: if you can&amp;rsquo;t explain why the code works, you shouldn&amp;rsquo;t be asking someone else to approve it.&lt;/p&gt;&#xA;&lt;p&gt;The organizations adapting best are investing heavily in what happens upstream of the pull request:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Architecture decision records and design docs&lt;/strong&gt; that agents and reviewers can reference&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Project rules files&lt;/strong&gt; (&lt;code&gt;.cursor/rules&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt;, &lt;code&gt;copilot-instructions.md&lt;/code&gt;) that encode team conventions so agents generate code that fits the system from the start&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Spec-driven development&lt;/strong&gt; where the specification is reviewed and approved before any code is generated&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Test-first workflows&lt;/strong&gt; where agents must execute and pass tests before submitting for review&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;When you invest in the inputs, you reduce the burden on the review. The review becomes a confirmation that the plan was followed, not a discovery process for whether the plan was any good.&lt;/p&gt;&#xA;&lt;h2 id=&#34;where-this-is-heading&#34;&gt;Where This Is Heading&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;m not saying we should stop reviewing code today, but I do think we&amp;rsquo;ll stop reviewing as much code in a year.&lt;/p&gt;&#xA;&lt;p&gt;The answer, I think, involves three shifts: let agents handle the mechanical review they&amp;rsquo;re now good enough at, invest heavily in what happens before code is written (specs, architecture, rules), and be intentional about preserving the knowledge-sharing function of review even if that means reviewing &lt;em&gt;less&lt;/em&gt; code, more &lt;em&gt;carefully&lt;/em&gt;, with a focus on system-level understanding rather than line-by-line correctness.&lt;/p&gt;&#xA;&lt;p&gt;The risk is knowledge erosion and cognitive debt. If humans stop reading code because agents write it and other agents review it, the team loses the shared understanding that code review has always quietly maintained. The Opsera 2026 benchmark found that senior engineers realize nearly &lt;a href=&#34;https://www.cjroth.com/blog/2026-02-18-building-an-elite-engineering-culture&#34;&gt;five times the productivity gains of junior engineers&lt;/a&gt; from AI tools — precisely because they already possess the system-level understanding needed to evaluate AI output. AI amplifies existing expertise. It doesn&amp;rsquo;t create it.&lt;/p&gt;&#xA;&lt;p&gt;So the question isn&amp;rsquo;t really &amp;ldquo;who will review all that code?&amp;rdquo; The question is: &lt;strong&gt;how do we maintain the knowledge transfer, architectural oversight, and shared understanding that code reviews have always provided — when the volume of code outstrips our capacity to read it all?&lt;/strong&gt;&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>The Rise of Hyper Local Software</title>
				<link>https://starkravingfinkle.org/posts/2026/02/the-rise-of-hyper-local-software/</link>
				<pubDate>Tue, 24 Feb 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/02/the-rise-of-hyper-local-software/</guid>
				<description>&lt;p&gt;For years, we&amp;rsquo;ve been told that software is eating the world. Big, cloud-hosted platforms — Salesforce, Notion, Slack, Jira, Airtable — working to solve every problem for every team, at any scale. And mostly, they delivered. SaaS transformed how businesses work.&lt;/p&gt;&#xA;&lt;p&gt;Making software and systems that support millions of people, with a variety of needs and requirements isn&amp;rsquo;t easy. Software adds complexity to deal with the hundreds use cases. It becomes a series of compromises. Software that starts small and well focused, evolves into a villain, despised by users.&lt;/p&gt;&#xA;&lt;p&gt;Now something is shifting and not enough people are talking about it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-saas-is-dead-take-is-half-right&#34;&gt;The &amp;ldquo;SaaS Is Dead&amp;rdquo; Take Is Half Right&lt;/h2&gt;&#xA;&lt;p&gt;There&amp;rsquo;s no shortage of hot takes about AI agents killing SaaS. The argument goes: why pay for Salesforce when an AI agent can just &lt;em&gt;build&lt;/em&gt; your CRM for you? Why subscribe to a project management tool when an agent can manage your projects? Satya Nadella has suggested that business applications are essentially just &amp;ldquo;CRUD databases with business logic&amp;rdquo; that will eventually collapse into the AI layer.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s a plausible long-term story for enterprise software, but it&amp;rsquo;s not the most interesting thing happening right now.&lt;/p&gt;&#xA;&lt;p&gt;The more interesting story is quieter and more personal. Agentic coding tools are making it trivially fast for individuals and small teams to build software &lt;em&gt;tailored to their situation&lt;/em&gt;. Not enterprise replacements. Not startups. Just&amp;hellip; tools that fit their needs.&lt;/p&gt;&#xA;&lt;p&gt;I don&amp;rsquo;t know if this has a name yet, but I&amp;rsquo;ve started calling it &lt;strong&gt;hyper-local software&lt;/strong&gt;: apps built for an audience that might be one person, one team, or one community. Apps that would never survive as a SaaS business because the market is too small or too specific, but that are useful to the people they&amp;rsquo;re built for.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-now&#34;&gt;Why Now&lt;/h2&gt;&#xA;&lt;p&gt;The economics of building software have changed. The friction of creation has dropped to the point where it now competes with the friction of &lt;em&gt;discovery&lt;/em&gt;. It used to be that if you had a niche software need, you&amp;rsquo;d spend hours searching for a tool that mostly fit, pay for it, and accept its limitations. Now, in many cases, it&amp;rsquo;s faster to just build what you actually want.&lt;/p&gt;&#xA;&lt;p&gt;Hyper-local software doesn&amp;rsquo;t have the same scale and quality requirements of enterprise SaaS software. When it&amp;rsquo;s software built for one, or a team, you&amp;rsquo;re willing to live with papercuts — or fix them as needed. You don&amp;rsquo;t need to handle scale for millions of users.&lt;/p&gt;&#xA;&lt;p&gt;The result is a new kind of software culture emerging. Not SaaS. Not open source in the traditional sense. Something more like the early days of the web, when people built things because they &lt;em&gt;wanted them&lt;/em&gt; and shared them because sharing was easy.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-opportunity&#34;&gt;The Opportunity&lt;/h2&gt;&#xA;&lt;p&gt;Is anyone thinking about how to &lt;em&gt;support&lt;/em&gt; this ecosystem?&lt;/p&gt;&#xA;&lt;p&gt;The conversation around AI and software is dominated by the enterprise angle, which SaaS companies will survive, which will be disrupted, how incumbents should respond. That&amp;rsquo;s a legitimate conversation, but it ignores an entirely different opportunity.&lt;/p&gt;&#xA;&lt;p&gt;What would it look like to deliberately cultivate a hyper-local software culture?&lt;/p&gt;&#xA;&lt;p&gt;The ingredients seem clear enough:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Low-friction creation.&lt;/strong&gt; The tools are largely here — agentic coding has dramatically lowered the barrier. But they&amp;rsquo;re still oriented toward professional developers. There&amp;rsquo;s room for tooling that&amp;rsquo;s oriented toward domain experts who want to build something that works for their specific context.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Easy sharing and remixing.&lt;/strong&gt; The most valuable thing about hyper-local apps isn&amp;rsquo;t just that one person can build one — it&amp;rsquo;s that &lt;em&gt;patterns&lt;/em&gt; can spread. If a teacher builds a great gradebook app for her specific situation, there&amp;rsquo;s likely another teacher in a slightly different situation who could build on that work. A culture of remixing is essential.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Community around use cases.&lt;/strong&gt; The social layer matters enormously. When people share what they&amp;rsquo;ve built, not as products but as &lt;em&gt;solutions to specific problems&lt;/em&gt;, it creates a library of starting points. It normalizes the idea of building your own tools. It creates the kind of culture where &amp;ldquo;I just built something for that&amp;rdquo; is a normal thing to say.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Graceful hosting.&lt;/strong&gt; The infrastructure needs to be low-maintenance enough that a non-engineer can deploy and keep running a small personal app without it becoming a second job.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t a product pitch. It&amp;rsquo;s a gap observation. The culture of hyper-local software is forming on its own in blog posts, in indie developer communities, in the growing practice of vibe coding. But it&amp;rsquo;s diffuse and unsupported.&lt;/p&gt;&#xA;&lt;p&gt;Has anyone thought about giving this ecosystem a home? Maybe I&amp;rsquo;m not paying close enough attention.&lt;/p&gt;&#xA;&lt;h2 id=&#34;glitch-was-almost-that-home&#34;&gt;Glitch Was Almost That Home&lt;/h2&gt;&#xA;&lt;p&gt;It&amp;rsquo;s hard for me to think about this gap without thinking about Glitch.&lt;/p&gt;&#xA;&lt;p&gt;Glitch launched in 2017 with a simple and radical idea: what if building and sharing a web app was as easy as publishing a blog post? You could create a project, remix someone else&amp;rsquo;s, deploy instantly, and share with a link. The community that formed around it was warm, creative, and genuinely collaborative. By 2019, the platform hosted over 2.5 million apps. Sadly, in 2025, Glitch shut down project hosting. The architecture had grown expensive to maintain. Abuse had driven up operational costs. The developer landscape had evolved around it.&lt;/p&gt;&#xA;&lt;p&gt;Looking back, Glitch had most of the right ingredients. Easy creation. One-click remixing. Starter projects that let you go from zero to something real in minutes. A community that celebrated building things, not just shipping products. An ethos&lt;sup&gt;1&lt;/sup&gt; of sharing that felt more like a craft community than a startup ecosystem.&lt;/p&gt;&#xA;&lt;p&gt;Glitch was, in spirit, exactly the kind of place where hyper-local software culture could have flourished. Maybe my own bias is showing.&lt;/p&gt;&#xA;&lt;p&gt;The best software has always been the kind that fits. The era of hyper-local apps is making that possible for almost anyone. The question is whether we&amp;rsquo;ll build the ecosystem to support it.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;1: Claude helped me with finding the right word&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;For years, we&amp;rsquo;ve been told that software is eating the world. Big, cloud-hosted platforms — Salesforce, Notion, Slack, Jira, Airtable — working to solve every problem for every team, at any scale. And mostly, they delivered. SaaS transformed how businesses work.&lt;/p&gt;&#xA;&lt;p&gt;Making software and systems that support millions of people, with a variety of needs and requirements isn&amp;rsquo;t easy. Software adds complexity to deal with the hundreds use cases. It becomes a series of compromises. Software that starts small and well focused, evolves into a villain, despised by users.&lt;/p&gt;&#xA;&lt;p&gt;Now something is shifting and not enough people are talking about it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-saas-is-dead-take-is-half-right&#34;&gt;The &amp;ldquo;SaaS Is Dead&amp;rdquo; Take Is Half Right&lt;/h2&gt;&#xA;&lt;p&gt;There&amp;rsquo;s no shortage of hot takes about AI agents killing SaaS. The argument goes: why pay for Salesforce when an AI agent can just &lt;em&gt;build&lt;/em&gt; your CRM for you? Why subscribe to a project management tool when an agent can manage your projects? Satya Nadella has suggested that business applications are essentially just &amp;ldquo;CRUD databases with business logic&amp;rdquo; that will eventually collapse into the AI layer.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s a plausible long-term story for enterprise software, but it&amp;rsquo;s not the most interesting thing happening right now.&lt;/p&gt;&#xA;&lt;p&gt;The more interesting story is quieter and more personal. Agentic coding tools are making it trivially fast for individuals and small teams to build software &lt;em&gt;tailored to their situation&lt;/em&gt;. Not enterprise replacements. Not startups. Just&amp;hellip; tools that fit their needs.&lt;/p&gt;&#xA;&lt;p&gt;I don&amp;rsquo;t know if this has a name yet, but I&amp;rsquo;ve started calling it &lt;strong&gt;hyper-local software&lt;/strong&gt;: apps built for an audience that might be one person, one team, or one community. Apps that would never survive as a SaaS business because the market is too small or too specific, but that are useful to the people they&amp;rsquo;re built for.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-now&#34;&gt;Why Now&lt;/h2&gt;&#xA;&lt;p&gt;The economics of building software have changed. The friction of creation has dropped to the point where it now competes with the friction of &lt;em&gt;discovery&lt;/em&gt;. It used to be that if you had a niche software need, you&amp;rsquo;d spend hours searching for a tool that mostly fit, pay for it, and accept its limitations. Now, in many cases, it&amp;rsquo;s faster to just build what you actually want.&lt;/p&gt;&#xA;&lt;p&gt;Hyper-local software doesn&amp;rsquo;t have the same scale and quality requirements of enterprise SaaS software. When it&amp;rsquo;s software built for one, or a team, you&amp;rsquo;re willing to live with papercuts — or fix them as needed. You don&amp;rsquo;t need to handle scale for millions of users.&lt;/p&gt;&#xA;&lt;p&gt;The result is a new kind of software culture emerging. Not SaaS. Not open source in the traditional sense. Something more like the early days of the web, when people built things because they &lt;em&gt;wanted them&lt;/em&gt; and shared them because sharing was easy.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-opportunity&#34;&gt;The Opportunity&lt;/h2&gt;&#xA;&lt;p&gt;Is anyone thinking about how to &lt;em&gt;support&lt;/em&gt; this ecosystem?&lt;/p&gt;&#xA;&lt;p&gt;The conversation around AI and software is dominated by the enterprise angle, which SaaS companies will survive, which will be disrupted, how incumbents should respond. That&amp;rsquo;s a legitimate conversation, but it ignores an entirely different opportunity.&lt;/p&gt;&#xA;&lt;p&gt;What would it look like to deliberately cultivate a hyper-local software culture?&lt;/p&gt;&#xA;&lt;p&gt;The ingredients seem clear enough:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Low-friction creation.&lt;/strong&gt; The tools are largely here — agentic coding has dramatically lowered the barrier. But they&amp;rsquo;re still oriented toward professional developers. There&amp;rsquo;s room for tooling that&amp;rsquo;s oriented toward domain experts who want to build something that works for their specific context.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Easy sharing and remixing.&lt;/strong&gt; The most valuable thing about hyper-local apps isn&amp;rsquo;t just that one person can build one — it&amp;rsquo;s that &lt;em&gt;patterns&lt;/em&gt; can spread. If a teacher builds a great gradebook app for her specific situation, there&amp;rsquo;s likely another teacher in a slightly different situation who could build on that work. A culture of remixing is essential.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Community around use cases.&lt;/strong&gt; The social layer matters enormously. When people share what they&amp;rsquo;ve built, not as products but as &lt;em&gt;solutions to specific problems&lt;/em&gt;, it creates a library of starting points. It normalizes the idea of building your own tools. It creates the kind of culture where &amp;ldquo;I just built something for that&amp;rdquo; is a normal thing to say.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Graceful hosting.&lt;/strong&gt; The infrastructure needs to be low-maintenance enough that a non-engineer can deploy and keep running a small personal app without it becoming a second job.&lt;/p&gt;&#xA;&lt;p&gt;This isn&amp;rsquo;t a product pitch. It&amp;rsquo;s a gap observation. The culture of hyper-local software is forming on its own in blog posts, in indie developer communities, in the growing practice of vibe coding. But it&amp;rsquo;s diffuse and unsupported.&lt;/p&gt;&#xA;&lt;p&gt;Has anyone thought about giving this ecosystem a home? Maybe I&amp;rsquo;m not paying close enough attention.&lt;/p&gt;&#xA;&lt;h2 id=&#34;glitch-was-almost-that-home&#34;&gt;Glitch Was Almost That Home&lt;/h2&gt;&#xA;&lt;p&gt;It&amp;rsquo;s hard for me to think about this gap without thinking about Glitch.&lt;/p&gt;&#xA;&lt;p&gt;Glitch launched in 2017 with a simple and radical idea: what if building and sharing a web app was as easy as publishing a blog post? You could create a project, remix someone else&amp;rsquo;s, deploy instantly, and share with a link. The community that formed around it was warm, creative, and genuinely collaborative. By 2019, the platform hosted over 2.5 million apps. Sadly, in 2025, Glitch shut down project hosting. The architecture had grown expensive to maintain. Abuse had driven up operational costs. The developer landscape had evolved around it.&lt;/p&gt;&#xA;&lt;p&gt;Looking back, Glitch had most of the right ingredients. Easy creation. One-click remixing. Starter projects that let you go from zero to something real in minutes. A community that celebrated building things, not just shipping products. An ethos&lt;sup&gt;1&lt;/sup&gt; of sharing that felt more like a craft community than a startup ecosystem.&lt;/p&gt;&#xA;&lt;p&gt;Glitch was, in spirit, exactly the kind of place where hyper-local software culture could have flourished. Maybe my own bias is showing.&lt;/p&gt;&#xA;&lt;p&gt;The best software has always been the kind that fits. The era of hyper-local apps is making that possible for almost anyone. The question is whether we&amp;rsquo;ll build the ecosystem to support it.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;1: Claude helped me with finding the right word&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Migrating a 20-Year WordPress Blog to Hugo and Cloudflare</title>
				<link>https://starkravingfinkle.org/posts/2026/02/migrating-wordpress-to-hugo-cloudflare/</link>
				<pubDate>Sun, 15 Feb 2026 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2026/02/migrating-wordpress-to-hugo-cloudflare/</guid>
				<description>&lt;p&gt;I moved my 20+ year old WordPress blog off Bluehost and onto a modern static site setup with Hugo and Cloudflare Workers. What I expected to be a painful, multi-week project turned into a surprisingly smooth 4-day migration, largely thanks to working with Claude as a coding agent throughout the process.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-move-away-from-wordpress&#34;&gt;Why Move Away from WordPress?&lt;/h2&gt;&#xA;&lt;p&gt;WordPress has served me well for two decades, but it felt like overkill for what is essentially a personal blog. I&amp;rsquo;m not running e-commerce, I don&amp;rsquo;t need a database for every page load, and I definitely don&amp;rsquo;t need the constant plugin updates and security concerns. More importantly, Bluehost, while reliable, isn&amp;rsquo;t exactly a modern hosting vendor. I wanted to explore what a more current tech stack could offer.&lt;/p&gt;&#xA;&lt;p&gt;This migration gave me an excuse to dive into Cloudflare&amp;rsquo;s ecosystem and see what they&amp;rsquo;re really capable of beyond just being a CDN. The cost savings (from ~$150/year to essentially free) was a nice outcome, but the real motivation was modernization and learning.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-export-process-wordpress-to-markdown&#34;&gt;The Export Process: WordPress to Markdown&lt;/h2&gt;&#xA;&lt;p&gt;The first step was getting content out of WordPress. I used the &lt;a href=&#34;https://simplystatic.com&#34;&gt;Simply Static&lt;/a&gt; plugin to export everything as static HTML files. This worked well and gave me a complete snapshot of the blog including all images, styles, and 20 years of posts. I decided to keep that as the main archive.&lt;/p&gt;&#xA;&lt;p&gt;But Hugo uses Markdown, not HTML. I used a Wordpress Export to extract all content into a download XML file. Then ran a &lt;a href=&#34;https://github.com/lonekorean/wordpress-export-to-markdown&#34;&gt;WordPress Export to Markdown&lt;/a&gt; tool on the export file.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx wordpress-export-to-markdown&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That created individual &lt;code&gt;index.md&lt;/code&gt; files for each post with proper front matter. Hugo is flexible about content structure. The WordPress Export to Markdown tool gave me options, and I decided to organize posts using a year/month hierarchy with page bundles:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;content/&#xA;└── posts/&#xA;    └── 2025/&#xA;        └── 03/&#xA;            └── exploring-llms-as-agents/&#xA;                ├── index.md&#xA;                └── images/&#xA;                    └── screenshot.png&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;strong&gt;page bundle&lt;/strong&gt; approach meant each post lived in its own folder with all its assets. Images could use relative paths like &lt;code&gt;images/screenshot.png&lt;/code&gt; in the Markdown, making posts completely self-contained.&lt;/p&gt;&#xA;&lt;p&gt;I chose the &lt;strong&gt;rusty-typewriter&lt;/strong&gt; theme as a starting point. Hugo&amp;rsquo;s theme system made it easy to customize without touching the theme files directly — just override partials in my own &lt;code&gt;layouts/&lt;/code&gt; directory when needed.&lt;/p&gt;&#xA;&lt;p&gt;A few things to note: I added &lt;strong&gt;series&lt;/strong&gt; as a custom taxonomy (great for multi-part posts), and configured a search index output so the theme&amp;rsquo;s built-in client-side search would work. The search feature was surprisingly fast once I optimized the index to only include post summaries instead of full content.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fixing-legacy-content&#34;&gt;Fixing Legacy Content&lt;/h2&gt;&#xA;&lt;p&gt;Twenty years of content meant dealing with some interesting legacy issues:&lt;/p&gt;&#xA;&lt;h3 id=&#34;cross-post-links&#34;&gt;Cross-Post Links&lt;/h3&gt;&#xA;&lt;p&gt;My WordPress URLs followed the pattern &lt;code&gt;/blog/YYYY/MM/post-slug/&lt;/code&gt;, but I wanted cleaner Hugo URLs at &lt;code&gt;/posts/YYYY/MM/post-slug/&lt;/code&gt;. Internal links needed updating:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Find and replace across all Markdown files&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find content/posts -name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*.md&amp;#34;&lt;/span&gt; -type f -exec sed -i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|https://starkravingfinkle.org/blog|/posts|g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; +&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ancient-blogspot-links&#34;&gt;Ancient Blogspot Links&lt;/h3&gt;&#xA;&lt;p&gt;I found posts that still linked to my &lt;em&gt;really&lt;/em&gt; old Blogspot blog from 2004. Those needed fixing too:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Handle multiple URLs on same line (trickier regex)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find content/posts -name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*.md&amp;#34;&lt;/span&gt; -type f -exec sed -i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|http://weborama\.blogspot\.com/\([^)]*\)\.html|/posts/\1/|g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; +&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key was using &lt;code&gt;[^)]&lt;/code&gt; in the regex pattern to stop at the closing parenthesis of Markdown links, preventing the pattern from being too greedy when multiple links appeared on one line.&lt;/p&gt;&#xA;&lt;h3 id=&#34;wordpress-shortcodes&#34;&gt;WordPress Shortcodes&lt;/h3&gt;&#xA;&lt;p&gt;WordPress uses &lt;code&gt;[caption]&lt;/code&gt; shortcodes that Hugo doesn&amp;rsquo;t understand. I converted them to Hugo&amp;rsquo;s native figure shortcode.&lt;/p&gt;&#xA;&lt;p&gt;I did some of these conversions manually for the handful of captioned images, but Claude created a script would work for larger volumes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;deploying-to-cloudflare-workers&#34;&gt;Deploying to Cloudflare Workers&lt;/h2&gt;&#xA;&lt;p&gt;Getting Hugo working locally was one thing; deploying to Cloudflare Workers was where I got to learn new things. Workers can serve static sites directly, which is perfect for a Hugo blog.&lt;/p&gt;&#xA;&lt;h3 id=&#34;basic-worker-setup&#34;&gt;Basic Worker Setup&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir starkravingfinkle-blog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd starkravingfinkle-blog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm init -y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install wrangler --save-dev&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;wrangler.toml&lt;/code&gt; configuration started simple:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;starkravingfinkle-blog&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;compatibility_date&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-02-04&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;assets&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./public&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;not_found_handling&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;404-page&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASSETS&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hugo builds into the &lt;code&gt;public/&lt;/code&gt; directory, and Workers serves from there. The &lt;code&gt;binding = &amp;quot;ASSETS&amp;quot;&lt;/code&gt; creates an environment variable you can reference in Worker code.&lt;/p&gt;&#xA;&lt;h3 id=&#34;handling-url-redirects&#34;&gt;Handling URL Redirects&lt;/h3&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s where it got more complex. My old WordPress URLs used &lt;code&gt;/blog/&lt;/code&gt; in the path, but I wanted the Hugo site at the root. Breaking existing links was not an option. There has to be a &lt;em&gt;few&lt;/em&gt; people who have linked to my posts over 20 years.&lt;/p&gt;&#xA;&lt;p&gt;I needed a Worker script to handle redirects:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// src/index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Redirect /blog/ paths to /posts/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// Handle RSS feed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/feed/&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/feed&amp;#39;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;redirect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;origin&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/feed.xml&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;301&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// Handle blog posts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/posts/&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;redirect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;origin&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;301&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Serve static assets normally&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ASSETS&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Updated &lt;code&gt;wrangler.toml&lt;/code&gt; to use the script:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;starkravingfinkle-blog&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src/index.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;compatibility_date&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-02-04&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;assets&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./public&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;not_found_handling&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;404-page&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASSETS&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now old WordPress URLs redirect to the new Hugo structure with proper 301 permanent redirects.&lt;/p&gt;&#xA;&lt;h3 id=&#34;deployment&#34;&gt;Deployment&lt;/h3&gt;&#xA;&lt;p&gt;The deployment workflow is simple:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Build the Hugo site&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hugo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Deploy to Cloudflare&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx wrangler deploy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. The site is live on Cloudflare&amp;rsquo;s edge network in seconds.&lt;/p&gt;&#xA;&lt;h2 id=&#34;dns-and-domain-migration&#34;&gt;DNS and Domain Migration&lt;/h2&gt;&#xA;&lt;p&gt;Moving from Bluehost to Cloudflare meant changing nameservers and setting up custom domains. The process was straightforward:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Add domain to Cloudflare (it scans existing DNS)&lt;/li&gt;&#xA;&lt;li&gt;Review imported DNS records&lt;/li&gt;&#xA;&lt;li&gt;Update nameservers at domain registrar&lt;/li&gt;&#xA;&lt;li&gt;Add custom domain to Worker (automatic DNS setup)&lt;/li&gt;&#xA;&lt;li&gt;Update &lt;code&gt;baseURL&lt;/code&gt; in &lt;code&gt;hugo.toml&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Rebuild and redeploy&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;DNS propagation took about 30 minutes in my case. I tested first with the &lt;code&gt;*.workers.dev&lt;/code&gt; subdomain before pointing the real domain, which gave me confidence everything worked.&lt;/p&gt;&#xA;&lt;p&gt;One gotcha: Browser DNS caching. Even after DNS propagated globally, my main browser held onto the old Bluehost IP for hours. Testing in Safari (not my main browser) showed the new site was live while Firefox stubbornly showed the old one. Clearing Firefox&amp;rsquo;s DNS cache (&lt;code&gt;about:networking#dns&lt;/code&gt;) fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;search-functionality&#34;&gt;Search Functionality&lt;/h2&gt;&#xA;&lt;p&gt;The rusty-typewriter theme includes client-side search using a JSON index. Initially, the search was slow because the index included full post content. Over 20 years of writing made for a huge JSON file.&lt;/p&gt;&#xA;&lt;p&gt;The fix was simple: generate summaries instead of full content in the search index template:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt;, &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;where&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;site&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RegularPages&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Params.indexable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ne&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;dict&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;permalink&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;delimit&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Params&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt; | &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;summary&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Summary&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;plainify&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Date&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Format&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Param&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dateformat&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2 January, 2006&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;jsonify&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Removing the full &lt;code&gt;.Plain&lt;/code&gt; content and using summaries made the search index 80-90% smaller and noticeably faster, but still gave very good search results&lt;/p&gt;&#xA;&lt;h2 id=&#34;working-with-claude-as-a-coding-agent&#34;&gt;Working with Claude as a Coding Agent&lt;/h2&gt;&#xA;&lt;p&gt;I need to call out how much easier this migration was because I worked with Claude throughout. I&amp;rsquo;m not talking about asking occasional questions, I mean having Claude actively participate in the entire migration as a coding agent.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;What Claude helped with:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Explaining Hugo&amp;rsquo;s content structure and conventions&lt;/li&gt;&#xA;&lt;li&gt;Writing and debugging sed commands for bulk URL fixes&lt;/li&gt;&#xA;&lt;li&gt;Creating the Worker redirect logic&lt;/li&gt;&#xA;&lt;li&gt;Troubleshooting DNS and configuration issues&lt;/li&gt;&#xA;&lt;li&gt;Optimizing the search index&lt;/li&gt;&#xA;&lt;li&gt;Walking through Cloudflare setup step-by-step&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The biggest value wasn&amp;rsquo;t just getting answers, it was the iterative problem-solving. When something didn&amp;rsquo;t work, we&amp;rsquo;d debug together. When I hit a wall, Claude suggested alternatives. When I learned something new about Hugo or Cloudflare, Claude helped connect it to the bigger picture.&lt;/p&gt;&#xA;&lt;p&gt;This is what AI coding agents excel at: removing the friction of getting started and maintaining momentum when you&amp;rsquo;re learning multiple new technologies at once. Without Claude, I probably would have spent a week just reading Hugo documentation and Cloudflare guides before writing any code. It might have been enough perceived friction that I just wouldn&amp;rsquo;t even have started.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;h3 id=&#34;static-sites-scale-down-complexity&#34;&gt;Static Sites Scale Down Complexity&lt;/h3&gt;&#xA;&lt;p&gt;Moving from WordPress to static HTML eliminated an entire class of concerns: database performance, PHP updates, plugin conflicts, security patches. The site is literally just files being served from Cloudflare&amp;rsquo;s edge network. Simple is powerful.&lt;/p&gt;&#xA;&lt;h3 id=&#34;redirects-were-important&#34;&gt;Redirects Were Important&lt;/h3&gt;&#xA;&lt;p&gt;Twenty years of links exist out there in the wild. Search engine indexes, social media posts, bookmarks, other blogs — all pointing to old URLs. The redirect strategy needed to be bulletproof. Cloudflare Workers made this trivial with a few lines of JavaScript.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ai-agents-change-the-learning-curve&#34;&gt;AI Agents Change the Learning Curve&lt;/h3&gt;&#xA;&lt;p&gt;Learning Hugo, Cloudflare Workers, DNS setup, and email configuration simultaneously would normally take weeks. With Claude as an active partner, it took 4 days. The agent didn&amp;rsquo;t just answer questions, it helped me make decisions, debug problems, and connect concepts across multiple technology stacks.&lt;/p&gt;&#xA;&lt;h3 id=&#34;legacy-content-has-character&#34;&gt;Legacy Content Has Character&lt;/h3&gt;&#xA;&lt;p&gt;Finding 20-year-old Blogspot links in my posts was a fun reminder of how long I&amp;rsquo;ve been writing online. Rather than being annoyed by the cleanup work, I appreciated the archeology of it. Every odd WordPress shortcode or malformed URL told a story about what blogging tools were like in 2005.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;Now that the blog is running on Hugo and Cloudflare, I want to explore a few additions:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;New content types&lt;/strong&gt;: Hugo makes it easy to add distinct content types beyond blog posts. I&amp;rsquo;m thinking about adding &lt;strong&gt;quotes&lt;/strong&gt; and &lt;strong&gt;TIL&lt;/strong&gt; (Today I Learned) sections — short-form content that doesn&amp;rsquo;t fit the typical blog post format.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Writing workflow&lt;/strong&gt;: Since the blog content is now local Markdown files (not trapped in a WordPress database), I can work more easily on drafts. The friction of &amp;ldquo;opening WordPress to write&amp;rdquo; is gone. It&amp;rsquo;s just files in a directory.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Analytics refinement&lt;/strong&gt;: Cloudflare Web Analytics is running, but I want to understand what reports and insights are actually useful versus vanity metrics. The data is there; I need to figure out what questions to ask of it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;Migrating a 20-year blog sounds daunting, but it turned out to be one of those projects that&amp;rsquo;s easier than you think, especially with the right tools. Hugo is powerful but approachable, Cloudflare Workers are remarkably simple to deploy to, and working with Claude as a coding agent removed most of the friction from learning new technologies.&lt;/p&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re on the fence about moving away from WordPress, I&amp;rsquo;d encourage you to try it. Static sites aren&amp;rsquo;t just faster and cheaper, they&amp;rsquo;re genuinely simpler to reason about. And with modern tooling (Hugo&amp;rsquo;s live reload, Wrangler&amp;rsquo;s instant deploys, AI agents for guidance), the developer experience is excellent.&lt;/p&gt;&#xA;&lt;p&gt;The web has come a long way since I started this blog in the early 2000s. It&amp;rsquo;s nice to have my infrastructure finally catch up.&lt;/p&gt;&#xA;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gohugo.io/documentation/&#34;&gt;Hugo Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://developers.cloudflare.com/workers/&#34;&gt;Cloudflare Workers Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/math-queiroz/rusty-typewriter&#34;&gt;Rusty Typewriter Hugo Theme&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I moved my 20+ year old WordPress blog off Bluehost and onto a modern static site setup with Hugo and Cloudflare Workers. What I expected to be a painful, multi-week project turned into a surprisingly smooth 4-day migration, largely thanks to working with Claude as a coding agent throughout the process.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-move-away-from-wordpress&#34;&gt;Why Move Away from WordPress?&lt;/h2&gt;&#xA;&lt;p&gt;WordPress has served me well for two decades, but it felt like overkill for what is essentially a personal blog. I&amp;rsquo;m not running e-commerce, I don&amp;rsquo;t need a database for every page load, and I definitely don&amp;rsquo;t need the constant plugin updates and security concerns. More importantly, Bluehost, while reliable, isn&amp;rsquo;t exactly a modern hosting vendor. I wanted to explore what a more current tech stack could offer.&lt;/p&gt;&#xA;&lt;p&gt;This migration gave me an excuse to dive into Cloudflare&amp;rsquo;s ecosystem and see what they&amp;rsquo;re really capable of beyond just being a CDN. The cost savings (from ~$150/year to essentially free) was a nice outcome, but the real motivation was modernization and learning.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-export-process-wordpress-to-markdown&#34;&gt;The Export Process: WordPress to Markdown&lt;/h2&gt;&#xA;&lt;p&gt;The first step was getting content out of WordPress. I used the &lt;a href=&#34;https://simplystatic.com&#34;&gt;Simply Static&lt;/a&gt; plugin to export everything as static HTML files. This worked well and gave me a complete snapshot of the blog including all images, styles, and 20 years of posts. I decided to keep that as the main archive.&lt;/p&gt;&#xA;&lt;p&gt;But Hugo uses Markdown, not HTML. I used a Wordpress Export to extract all content into a download XML file. Then ran a &lt;a href=&#34;https://github.com/lonekorean/wordpress-export-to-markdown&#34;&gt;WordPress Export to Markdown&lt;/a&gt; tool on the export file.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx wordpress-export-to-markdown&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That created individual &lt;code&gt;index.md&lt;/code&gt; files for each post with proper front matter. Hugo is flexible about content structure. The WordPress Export to Markdown tool gave me options, and I decided to organize posts using a year/month hierarchy with page bundles:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;content/&#xA;└── posts/&#xA;    └── 2025/&#xA;        └── 03/&#xA;            └── exploring-llms-as-agents/&#xA;                ├── index.md&#xA;                └── images/&#xA;                    └── screenshot.png&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This &lt;strong&gt;page bundle&lt;/strong&gt; approach meant each post lived in its own folder with all its assets. Images could use relative paths like &lt;code&gt;images/screenshot.png&lt;/code&gt; in the Markdown, making posts completely self-contained.&lt;/p&gt;&#xA;&lt;p&gt;I chose the &lt;strong&gt;rusty-typewriter&lt;/strong&gt; theme as a starting point. Hugo&amp;rsquo;s theme system made it easy to customize without touching the theme files directly — just override partials in my own &lt;code&gt;layouts/&lt;/code&gt; directory when needed.&lt;/p&gt;&#xA;&lt;p&gt;A few things to note: I added &lt;strong&gt;series&lt;/strong&gt; as a custom taxonomy (great for multi-part posts), and configured a search index output so the theme&amp;rsquo;s built-in client-side search would work. The search feature was surprisingly fast once I optimized the index to only include post summaries instead of full content.&lt;/p&gt;&#xA;&lt;h2 id=&#34;fixing-legacy-content&#34;&gt;Fixing Legacy Content&lt;/h2&gt;&#xA;&lt;p&gt;Twenty years of content meant dealing with some interesting legacy issues:&lt;/p&gt;&#xA;&lt;h3 id=&#34;cross-post-links&#34;&gt;Cross-Post Links&lt;/h3&gt;&#xA;&lt;p&gt;My WordPress URLs followed the pattern &lt;code&gt;/blog/YYYY/MM/post-slug/&lt;/code&gt;, but I wanted cleaner Hugo URLs at &lt;code&gt;/posts/YYYY/MM/post-slug/&lt;/code&gt;. Internal links needed updating:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Find and replace across all Markdown files&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find content/posts -name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*.md&amp;#34;&lt;/span&gt; -type f -exec sed -i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|https://starkravingfinkle.org/blog|/posts|g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; +&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;ancient-blogspot-links&#34;&gt;Ancient Blogspot Links&lt;/h3&gt;&#xA;&lt;p&gt;I found posts that still linked to my &lt;em&gt;really&lt;/em&gt; old Blogspot blog from 2004. Those needed fixing too:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Handle multiple URLs on same line (trickier regex)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;find content/posts -name &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;*.md&amp;#34;&lt;/span&gt; -type f -exec sed -i &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;s|http://weborama\.blogspot\.com/\([^)]*\)\.html|/posts/\1/|g&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{}&lt;/span&gt; +&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key was using &lt;code&gt;[^)]&lt;/code&gt; in the regex pattern to stop at the closing parenthesis of Markdown links, preventing the pattern from being too greedy when multiple links appeared on one line.&lt;/p&gt;&#xA;&lt;h3 id=&#34;wordpress-shortcodes&#34;&gt;WordPress Shortcodes&lt;/h3&gt;&#xA;&lt;p&gt;WordPress uses &lt;code&gt;[caption]&lt;/code&gt; shortcodes that Hugo doesn&amp;rsquo;t understand. I converted them to Hugo&amp;rsquo;s native figure shortcode.&lt;/p&gt;&#xA;&lt;p&gt;I did some of these conversions manually for the handful of captioned images, but Claude created a script would work for larger volumes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;deploying-to-cloudflare-workers&#34;&gt;Deploying to Cloudflare Workers&lt;/h2&gt;&#xA;&lt;p&gt;Getting Hugo working locally was one thing; deploying to Cloudflare Workers was where I got to learn new things. Workers can serve static sites directly, which is perfect for a Hugo blog.&lt;/p&gt;&#xA;&lt;h3 id=&#34;basic-worker-setup&#34;&gt;Basic Worker Setup&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;mkdir starkravingfinkle-blog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;cd starkravingfinkle-blog&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm init -y&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npm install wrangler --save-dev&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;wrangler.toml&lt;/code&gt; configuration started simple:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;starkravingfinkle-blog&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;compatibility_date&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-02-04&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;assets&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./public&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;not_found_handling&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;404-page&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASSETS&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hugo builds into the &lt;code&gt;public/&lt;/code&gt; directory, and Workers serves from there. The &lt;code&gt;binding = &amp;quot;ASSETS&amp;quot;&lt;/code&gt; creates an environment variable you can reference in Worker code.&lt;/p&gt;&#xA;&lt;h3 id=&#34;handling-url-redirects&#34;&gt;Handling URL Redirects&lt;/h3&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s where it got more complex. My old WordPress URLs used &lt;code&gt;/blog/&lt;/code&gt; in the path, but I wanted the Hugo site at the root. Breaking existing links was not an option. There has to be a &lt;em&gt;few&lt;/em&gt; people who have linked to my posts over 20 years.&lt;/p&gt;&#xA;&lt;p&gt;I needed a Worker script to handle redirects:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// src/index.js&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Redirect /blog/ paths to /posts/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;)) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// Handle RSS feed&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/feed/&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;||&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/feed&amp;#39;&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;redirect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;origin&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/feed.xml&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;301&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// Handle blog posts&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/blog/&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;/posts/&amp;#39;&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Response&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;redirect&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;origin&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;newPath&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;301&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// Serve static assets normally&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ASSETS&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;);&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Updated &lt;code&gt;wrangler.toml&lt;/code&gt; to use the script:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;starkravingfinkle-blog&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;src/index.js&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;compatibility_date&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2025-02-04&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;assets&lt;/span&gt;]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;directory&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./public&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;not_found_handling&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;404-page&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;binding&lt;/span&gt; = &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ASSETS&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now old WordPress URLs redirect to the new Hugo structure with proper 301 permanent redirects.&lt;/p&gt;&#xA;&lt;h3 id=&#34;deployment&#34;&gt;Deployment&lt;/h3&gt;&#xA;&lt;p&gt;The deployment workflow is simple:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Build the Hugo site&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;hugo&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Deploy to Cloudflare&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;npx wrangler deploy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. The site is live on Cloudflare&amp;rsquo;s edge network in seconds.&lt;/p&gt;&#xA;&lt;h2 id=&#34;dns-and-domain-migration&#34;&gt;DNS and Domain Migration&lt;/h2&gt;&#xA;&lt;p&gt;Moving from Bluehost to Cloudflare meant changing nameservers and setting up custom domains. The process was straightforward:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Add domain to Cloudflare (it scans existing DNS)&lt;/li&gt;&#xA;&lt;li&gt;Review imported DNS records&lt;/li&gt;&#xA;&lt;li&gt;Update nameservers at domain registrar&lt;/li&gt;&#xA;&lt;li&gt;Add custom domain to Worker (automatic DNS setup)&lt;/li&gt;&#xA;&lt;li&gt;Update &lt;code&gt;baseURL&lt;/code&gt; in &lt;code&gt;hugo.toml&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Rebuild and redeploy&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;DNS propagation took about 30 minutes in my case. I tested first with the &lt;code&gt;*.workers.dev&lt;/code&gt; subdomain before pointing the real domain, which gave me confidence everything worked.&lt;/p&gt;&#xA;&lt;p&gt;One gotcha: Browser DNS caching. Even after DNS propagated globally, my main browser held onto the old Bluehost IP for hours. Testing in Safari (not my main browser) showed the new site was live while Firefox stubbornly showed the old one. Clearing Firefox&amp;rsquo;s DNS cache (&lt;code&gt;about:networking#dns&lt;/code&gt;) fixed it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;search-functionality&#34;&gt;Search Functionality&lt;/h2&gt;&#xA;&lt;p&gt;The rusty-typewriter theme includes client-side search using a JSON index. Initially, the search was slow because the index included full post content. Over 20 years of writing made for a huge JSON file.&lt;/p&gt;&#xA;&lt;p&gt;The fix was simple: generate summaries instead of full content in the search index template:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;range&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt;, &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;where&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;site&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RegularPages&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Params.indexable&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ne&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;dict&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;index&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;permalink&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;RelPermalink&lt;/span&gt; &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;delimit&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Params&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt; | &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;slice&lt;/span&gt;) &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;) &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;summary&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Summary&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;plainify&lt;/span&gt;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;date&amp;#34;&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;element&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Date&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Format&lt;/span&gt; (&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Param&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;dateformat&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#66d9ef&#34;&gt;default&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2 January, 2006&amp;#34;&lt;/span&gt;))&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ) &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Scratch&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;searchindex&amp;#34;&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;jsonify&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;}}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Removing the full &lt;code&gt;.Plain&lt;/code&gt; content and using summaries made the search index 80-90% smaller and noticeably faster, but still gave very good search results&lt;/p&gt;&#xA;&lt;h2 id=&#34;working-with-claude-as-a-coding-agent&#34;&gt;Working with Claude as a Coding Agent&lt;/h2&gt;&#xA;&lt;p&gt;I need to call out how much easier this migration was because I worked with Claude throughout. I&amp;rsquo;m not talking about asking occasional questions, I mean having Claude actively participate in the entire migration as a coding agent.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;What Claude helped with:&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Explaining Hugo&amp;rsquo;s content structure and conventions&lt;/li&gt;&#xA;&lt;li&gt;Writing and debugging sed commands for bulk URL fixes&lt;/li&gt;&#xA;&lt;li&gt;Creating the Worker redirect logic&lt;/li&gt;&#xA;&lt;li&gt;Troubleshooting DNS and configuration issues&lt;/li&gt;&#xA;&lt;li&gt;Optimizing the search index&lt;/li&gt;&#xA;&lt;li&gt;Walking through Cloudflare setup step-by-step&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The biggest value wasn&amp;rsquo;t just getting answers, it was the iterative problem-solving. When something didn&amp;rsquo;t work, we&amp;rsquo;d debug together. When I hit a wall, Claude suggested alternatives. When I learned something new about Hugo or Cloudflare, Claude helped connect it to the bigger picture.&lt;/p&gt;&#xA;&lt;p&gt;This is what AI coding agents excel at: removing the friction of getting started and maintaining momentum when you&amp;rsquo;re learning multiple new technologies at once. Without Claude, I probably would have spent a week just reading Hugo documentation and Cloudflare guides before writing any code. It might have been enough perceived friction that I just wouldn&amp;rsquo;t even have started.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;h3 id=&#34;static-sites-scale-down-complexity&#34;&gt;Static Sites Scale Down Complexity&lt;/h3&gt;&#xA;&lt;p&gt;Moving from WordPress to static HTML eliminated an entire class of concerns: database performance, PHP updates, plugin conflicts, security patches. The site is literally just files being served from Cloudflare&amp;rsquo;s edge network. Simple is powerful.&lt;/p&gt;&#xA;&lt;h3 id=&#34;redirects-were-important&#34;&gt;Redirects Were Important&lt;/h3&gt;&#xA;&lt;p&gt;Twenty years of links exist out there in the wild. Search engine indexes, social media posts, bookmarks, other blogs — all pointing to old URLs. The redirect strategy needed to be bulletproof. Cloudflare Workers made this trivial with a few lines of JavaScript.&lt;/p&gt;&#xA;&lt;h3 id=&#34;ai-agents-change-the-learning-curve&#34;&gt;AI Agents Change the Learning Curve&lt;/h3&gt;&#xA;&lt;p&gt;Learning Hugo, Cloudflare Workers, DNS setup, and email configuration simultaneously would normally take weeks. With Claude as an active partner, it took 4 days. The agent didn&amp;rsquo;t just answer questions, it helped me make decisions, debug problems, and connect concepts across multiple technology stacks.&lt;/p&gt;&#xA;&lt;h3 id=&#34;legacy-content-has-character&#34;&gt;Legacy Content Has Character&lt;/h3&gt;&#xA;&lt;p&gt;Finding 20-year-old Blogspot links in my posts was a fun reminder of how long I&amp;rsquo;ve been writing online. Rather than being annoyed by the cleanup work, I appreciated the archeology of it. Every odd WordPress shortcode or malformed URL told a story about what blogging tools were like in 2005.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;Now that the blog is running on Hugo and Cloudflare, I want to explore a few additions:&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;New content types&lt;/strong&gt;: Hugo makes it easy to add distinct content types beyond blog posts. I&amp;rsquo;m thinking about adding &lt;strong&gt;quotes&lt;/strong&gt; and &lt;strong&gt;TIL&lt;/strong&gt; (Today I Learned) sections — short-form content that doesn&amp;rsquo;t fit the typical blog post format.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Writing workflow&lt;/strong&gt;: Since the blog content is now local Markdown files (not trapped in a WordPress database), I can work more easily on drafts. The friction of &amp;ldquo;opening WordPress to write&amp;rdquo; is gone. It&amp;rsquo;s just files in a directory.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Analytics refinement&lt;/strong&gt;: Cloudflare Web Analytics is running, but I want to understand what reports and insights are actually useful versus vanity metrics. The data is there; I need to figure out what questions to ask of it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;final-thoughts&#34;&gt;Final Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;Migrating a 20-year blog sounds daunting, but it turned out to be one of those projects that&amp;rsquo;s easier than you think, especially with the right tools. Hugo is powerful but approachable, Cloudflare Workers are remarkably simple to deploy to, and working with Claude as a coding agent removed most of the friction from learning new technologies.&lt;/p&gt;&#xA;&lt;p&gt;If you&amp;rsquo;re on the fence about moving away from WordPress, I&amp;rsquo;d encourage you to try it. Static sites aren&amp;rsquo;t just faster and cheaper, they&amp;rsquo;re genuinely simpler to reason about. And with modern tooling (Hugo&amp;rsquo;s live reload, Wrangler&amp;rsquo;s instant deploys, AI agents for guidance), the developer experience is excellent.&lt;/p&gt;&#xA;&lt;p&gt;The web has come a long way since I started this blog in the early 2000s. It&amp;rsquo;s nice to have my infrastructure finally catch up.&lt;/p&gt;&#xA;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://gohugo.io/documentation/&#34;&gt;Hugo Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://developers.cloudflare.com/workers/&#34;&gt;Cloudflare Workers Documentation&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/math-queiroz/rusty-typewriter&#34;&gt;Rusty Typewriter Hugo Theme&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Agent Coding Update: Teaching Agents Our Primitives</title>
				<link>https://starkravingfinkle.org/posts/2025/12/agent-coding-update-teaching-agents-our-primitives/</link>
				<pubDate>Mon, 08 Dec 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/12/agent-coding-update-teaching-agents-our-primitives/</guid>
				<description>&lt;p&gt;A few months ago, I wrote about &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/07/vibe-coded-to-shipped-feature/&#34;&gt;vibe coding and the challenge of shipping quickly-created prototypes&lt;/a&gt;. I ended that post suggesting we could &amp;ldquo;teach&amp;rdquo; coding agents about our platform primitives through instruction files and MCP servers. At my day job (League), we&amp;rsquo;ve been putting this idea into practice, and the results have been very encouraging.&lt;/p&gt;&#xA;&lt;h3 id=&#34;building-a-knowledge-base-for-agents&#34;&gt;Building a Knowledge Base for Agents&lt;/h3&gt;&#xA;&lt;p&gt;A cross-functional team has created a structured knowledge base repository—a collection of markdown documents covering both frontend and backend aspects of the League platform. Think of it as documentation specifically designed for agent consumption. These aren&amp;rsquo;t just API references or code comments; they&amp;rsquo;re curated explanations of our patterns, primitives, and platform conventions.&lt;/p&gt;&#xA;&lt;p&gt;The knowledge base includes information about:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UI frameworks and component patterns we use&lt;/li&gt;&#xA;&lt;li&gt;Backend integration patterns and workflows&lt;/li&gt;&#xA;&lt;li&gt;API structures and authentication mechanisms&lt;/li&gt;&#xA;&lt;li&gt;Routing and state management patterns&lt;/li&gt;&#xA;&lt;li&gt;Testing strategies and common utilities&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;making-knowledge-discoverable-and-actionable&#34;&gt;Making Knowledge Discoverable and Actionable&lt;/h3&gt;&#xA;&lt;p&gt;Documentation is only useful if it can be found. The team built an MCP server that gives agents access to some useful capabilities:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Search&lt;/strong&gt; – Agents can query the knowledge base by topic, finding relevant documents from our extensive collection without needing to know exactly what exists.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Read&lt;/strong&gt; - Agents can fetch the contents of the relevant documents for use in the current working session.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Write&lt;/strong&gt; – Most importantly, agents can contribute back to the knowledge base. When an agent, working with a developer, discovers a successful pattern or solves a problem in a novel way, it can propose new documentation via pull request.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This bidirectional flow transforms the knowledge base from static documentation into a living, growing resource. The agents aren&amp;rsquo;t just consuming our platform knowledge, they&amp;rsquo;re helping to build it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;local-workflows-enable-faster-iteration&#34;&gt;Local Workflows Enable Faster Iteration&lt;/h3&gt;&#xA;&lt;p&gt;One of our key learnings has been that making more of the developer workflow &amp;ldquo;local&amp;rdquo; dramatically improves agent effectiveness. When an agent can not only write code but also test the changes immediately, the feedback loop tightens significantly. The agent, and developer, learns what works faster and produces better outcomes with less back-and-forth.&lt;/p&gt;&#xA;&lt;p&gt;This local-first approach also makes agents better at completing tasks. As they experiment and validate patterns, they can immediately capture that knowledge and push it back into the knowledge base. Good patterns get documented while they&amp;rsquo;re fresh. Failed approaches can be noted before anyone else tries them.&lt;/p&gt;&#xA;&lt;h3 id=&#34;from-prototype-to-production-faster&#34;&gt;From Prototype to Production, Faster&lt;/h3&gt;&#xA;&lt;p&gt;The combination of searchable platform knowledge and local testing capabilities has transformed how quickly we can go from idea to working feature. Developers, working with agents, now vibe code using &lt;em&gt;our&lt;/em&gt; primitives from the start. There&amp;rsquo;s less &amp;ldquo;throw away the prototype and rebuild it properly&amp;rdquo; and more &amp;ldquo;this is already using our patterns, let&amp;rsquo;s polish and ship.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;The code that emerges from the agent looks like it could have been written by someone familiar with League&amp;rsquo;s platform conventions because in a very real sense, it was. The agent had access to our collective knowledge about what good League platform code looks like.&lt;/p&gt;&#xA;&lt;p&gt;If you think it&amp;rsquo;s fun to vibe code green-field prototypes built on default frameworks not connected to your own system, I can tell you it&amp;rsquo;s even more fun to quickly prototype and craft solutions using your own primitives, ready to be added into your products.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-were-learning&#34;&gt;What We&amp;rsquo;re Learning&lt;/h3&gt;&#xA;&lt;p&gt;This approach isn&amp;rsquo;t without challenges. We&amp;rsquo;re still discovering:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;What level of detail makes documentation most useful for agents versus overwhelming the context window. Developer curation plays a critical role.&lt;/li&gt;&#xA;&lt;li&gt;How to structure the knowledge base so relevant information surfaces easily. Again, developer curation along with solid search capabilities should help.&lt;/li&gt;&#xA;&lt;li&gt;When to trust agent-written documentation versus requiring developer review. At this point, we&amp;rsquo;re always requiring developer review, and that might be the case permanently.&lt;/li&gt;&#xA;&lt;li&gt;How to balance comprehensiveness with maintainability as the knowledge base grows.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;But the core insight holds: when you give agents knowledge of your platform primitives and enable them to both test and contribute, they become far more effective collaborators. The coding isn&amp;rsquo;t just faster, it&amp;rsquo;s actually aligned with how you build software.&lt;/p&gt;&#xA;&lt;h3 id=&#34;where-were-headed&#34;&gt;Where We&amp;rsquo;re Headed&lt;/h3&gt;&#xA;&lt;p&gt;We&amp;rsquo;re still early in this exploration, but the path is promising. As the knowledge base grows and agents contribute more successful patterns, we&amp;rsquo;re creating a virtuous cycle. Each successful feature makes the next one easier. Each documented pattern helps future agents avoid past mistakes. The main goal to help developers get more done without as much cognitive load.&lt;/p&gt;&#xA;&lt;p&gt;The future of agentic development won&amp;rsquo;t be about replacing developers. It will be about building systems where developer knowledge and machine capability amplify each other. Where the tribal knowledge that usually lives in senior developers&amp;rsquo; heads gets captured, structured, and made accessible at the moment it&amp;rsquo;s needed.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;A few months ago, I wrote about &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/07/vibe-coded-to-shipped-feature/&#34;&gt;vibe coding and the challenge of shipping quickly-created prototypes&lt;/a&gt;. I ended that post suggesting we could &amp;ldquo;teach&amp;rdquo; coding agents about our platform primitives through instruction files and MCP servers. At my day job (League), we&amp;rsquo;ve been putting this idea into practice, and the results have been very encouraging.&lt;/p&gt;&#xA;&lt;h3 id=&#34;building-a-knowledge-base-for-agents&#34;&gt;Building a Knowledge Base for Agents&lt;/h3&gt;&#xA;&lt;p&gt;A cross-functional team has created a structured knowledge base repository—a collection of markdown documents covering both frontend and backend aspects of the League platform. Think of it as documentation specifically designed for agent consumption. These aren&amp;rsquo;t just API references or code comments; they&amp;rsquo;re curated explanations of our patterns, primitives, and platform conventions.&lt;/p&gt;&#xA;&lt;p&gt;The knowledge base includes information about:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;UI frameworks and component patterns we use&lt;/li&gt;&#xA;&lt;li&gt;Backend integration patterns and workflows&lt;/li&gt;&#xA;&lt;li&gt;API structures and authentication mechanisms&lt;/li&gt;&#xA;&lt;li&gt;Routing and state management patterns&lt;/li&gt;&#xA;&lt;li&gt;Testing strategies and common utilities&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;making-knowledge-discoverable-and-actionable&#34;&gt;Making Knowledge Discoverable and Actionable&lt;/h3&gt;&#xA;&lt;p&gt;Documentation is only useful if it can be found. The team built an MCP server that gives agents access to some useful capabilities:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Search&lt;/strong&gt; – Agents can query the knowledge base by topic, finding relevant documents from our extensive collection without needing to know exactly what exists.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Read&lt;/strong&gt; - Agents can fetch the contents of the relevant documents for use in the current working session.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Write&lt;/strong&gt; – Most importantly, agents can contribute back to the knowledge base. When an agent, working with a developer, discovers a successful pattern or solves a problem in a novel way, it can propose new documentation via pull request.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This bidirectional flow transforms the knowledge base from static documentation into a living, growing resource. The agents aren&amp;rsquo;t just consuming our platform knowledge, they&amp;rsquo;re helping to build it.&lt;/p&gt;&#xA;&lt;h3 id=&#34;local-workflows-enable-faster-iteration&#34;&gt;Local Workflows Enable Faster Iteration&lt;/h3&gt;&#xA;&lt;p&gt;One of our key learnings has been that making more of the developer workflow &amp;ldquo;local&amp;rdquo; dramatically improves agent effectiveness. When an agent can not only write code but also test the changes immediately, the feedback loop tightens significantly. The agent, and developer, learns what works faster and produces better outcomes with less back-and-forth.&lt;/p&gt;&#xA;&lt;p&gt;This local-first approach also makes agents better at completing tasks. As they experiment and validate patterns, they can immediately capture that knowledge and push it back into the knowledge base. Good patterns get documented while they&amp;rsquo;re fresh. Failed approaches can be noted before anyone else tries them.&lt;/p&gt;&#xA;&lt;h3 id=&#34;from-prototype-to-production-faster&#34;&gt;From Prototype to Production, Faster&lt;/h3&gt;&#xA;&lt;p&gt;The combination of searchable platform knowledge and local testing capabilities has transformed how quickly we can go from idea to working feature. Developers, working with agents, now vibe code using &lt;em&gt;our&lt;/em&gt; primitives from the start. There&amp;rsquo;s less &amp;ldquo;throw away the prototype and rebuild it properly&amp;rdquo; and more &amp;ldquo;this is already using our patterns, let&amp;rsquo;s polish and ship.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;The code that emerges from the agent looks like it could have been written by someone familiar with League&amp;rsquo;s platform conventions because in a very real sense, it was. The agent had access to our collective knowledge about what good League platform code looks like.&lt;/p&gt;&#xA;&lt;p&gt;If you think it&amp;rsquo;s fun to vibe code green-field prototypes built on default frameworks not connected to your own system, I can tell you it&amp;rsquo;s even more fun to quickly prototype and craft solutions using your own primitives, ready to be added into your products.&lt;/p&gt;&#xA;&lt;h3 id=&#34;what-were-learning&#34;&gt;What We&amp;rsquo;re Learning&lt;/h3&gt;&#xA;&lt;p&gt;This approach isn&amp;rsquo;t without challenges. We&amp;rsquo;re still discovering:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;What level of detail makes documentation most useful for agents versus overwhelming the context window. Developer curation plays a critical role.&lt;/li&gt;&#xA;&lt;li&gt;How to structure the knowledge base so relevant information surfaces easily. Again, developer curation along with solid search capabilities should help.&lt;/li&gt;&#xA;&lt;li&gt;When to trust agent-written documentation versus requiring developer review. At this point, we&amp;rsquo;re always requiring developer review, and that might be the case permanently.&lt;/li&gt;&#xA;&lt;li&gt;How to balance comprehensiveness with maintainability as the knowledge base grows.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;But the core insight holds: when you give agents knowledge of your platform primitives and enable them to both test and contribute, they become far more effective collaborators. The coding isn&amp;rsquo;t just faster, it&amp;rsquo;s actually aligned with how you build software.&lt;/p&gt;&#xA;&lt;h3 id=&#34;where-were-headed&#34;&gt;Where We&amp;rsquo;re Headed&lt;/h3&gt;&#xA;&lt;p&gt;We&amp;rsquo;re still early in this exploration, but the path is promising. As the knowledge base grows and agents contribute more successful patterns, we&amp;rsquo;re creating a virtuous cycle. Each successful feature makes the next one easier. Each documented pattern helps future agents avoid past mistakes. The main goal to help developers get more done without as much cognitive load.&lt;/p&gt;&#xA;&lt;p&gt;The future of agentic development won&amp;rsquo;t be about replacing developers. It will be about building systems where developer knowledge and machine capability amplify each other. Where the tribal knowledge that usually lives in senior developers&amp;rsquo; heads gets captured, structured, and made accessible at the moment it&amp;rsquo;s needed.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Vibe Coded to Shipped Feature</title>
				<link>https://starkravingfinkle.org/posts/2025/07/vibe-coded-to-shipped-feature/</link>
				<pubDate>Sun, 06 Jul 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/07/vibe-coded-to-shipped-feature/</guid>
				<description>&lt;p&gt;AI-assisted coding tools and agents have unlocked an unprecedented level of functional prototyping. Not just for developers. Product managers, Designers, even the C-suite are building functional prototypes of their ideas in a matter of days, or even hours.&lt;/p&gt;&#xA;&lt;p&gt;There can be a lot of value in creating a functional prototype of an idea so people can really test out the interactions and experience in ways that are not feasible with less interactive approaches.&lt;/p&gt;&#xA;&lt;p&gt;People like to push boundaries, so I imagine a future, maybe it&amp;rsquo;s already here, where people begin to ask: &lt;strong&gt;How do I ship my quickly created prototype in our product?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/vibe-code-feature-min.png&#34; alt=&#34;gru plan meme: vibe-code a feature; feature is cool; ship the feature (assertive); ship the feature (scared)&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Lots of people will tell you that you can&amp;rsquo;t just ship a vibe coded feature — well, maybe those &amp;ldquo;people&amp;rdquo; are developers. There are some good reasons why you should think twice about shipping a vibe coded concept:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Do you understand the code the agent wrote?&lt;/li&gt;&#xA;&lt;li&gt;What dependencies does the application use?&lt;/li&gt;&#xA;&lt;li&gt;How does it store data and is it secure?&lt;/li&gt;&#xA;&lt;li&gt;How does the login system work, if it has one?&lt;/li&gt;&#xA;&lt;li&gt;… the list goes on&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Honestly, the same questions should be asked no matter how the code was created. If the vibe coded prototype is the MVP, then you have many options, perhaps even just shipping the prototype as-is.&lt;/p&gt;&#xA;&lt;p&gt;Typically, we&amp;rsquo;re in a situation where we already have an existing platform or application. We can&amp;rsquo;t just ship the prototype. We need to somehow merge the prototype into the existing code base.&lt;/p&gt;&#xA;&lt;p&gt;So the more important question could be: &lt;strong&gt;How do we integrate this code into our existing platform or application?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Existing platforms and applications have data stores, UI frameworks, routing mechanisms, APIs, and other &amp;ldquo;primitives&amp;rdquo; used to create and evolve the systems.&lt;/p&gt;&#xA;&lt;p&gt;We have some options.&lt;/p&gt;&#xA;&lt;h1 id=&#34;breakdown-and-rebuild&#34;&gt;Breakdown and Rebuild&lt;/h1&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s treat the prototype as exactly that — a quickly created example of what we want to deliver. With this option we should audit the prototype and decompose it into the pieces used to create it.  Once we have a breakdown of the system, we can start re-composing the concept using our own primitives.&lt;/p&gt;&#xA;&lt;p&gt;This approach is what we&amp;rsquo;ve been doing for years for hastily created prototypes created during hackathons or discovery spikes. The process won&amp;rsquo;t feel any faster and certainly doesn&amp;rsquo;t feel &amp;ldquo;agentic&amp;rdquo;, but it works.&lt;/p&gt;&#xA;&lt;h1 id=&#34;vibe-code-with-our-primitives&#34;&gt;Vibe Code with Our Primitives &lt;/h1&gt;&#xA;&lt;p&gt;What if we could &amp;ldquo;teach&amp;rdquo; the vibe coding agent about the primitives we use?  It could use our UI framework and APIs. It could understand our authentication system. It could store data into our data stores.&lt;/p&gt;&#xA;&lt;p&gt;Agentic code assistants like Cursor and Copilot have &lt;a href=&#34;https://docs.cursor.com/context/rules&#34;&gt;rule&lt;/a&gt; or &lt;a href=&#34;https://docs.github.com/en/copilot/how-tos/custom-instructions/adding-repository-custom-instructions-for-github-copilot&#34;&gt;instruction&lt;/a&gt; files that can provide guidance and details for how the agent constructs the code, and can include code snippets and examples.&lt;/p&gt;&#xA;&lt;p&gt;Agents can also use MCP servers that can be used to provide more semantic content to the agent about the frameworks and systems already in place.&lt;/p&gt;&#xA;&lt;p&gt;This approach is how we create the most impact and productivity from  code assistant tools. We might not be at this point yet, but we keep getting closer.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;AI-assisted coding tools and agents have unlocked an unprecedented level of functional prototyping. Not just for developers. Product managers, Designers, even the C-suite are building functional prototypes of their ideas in a matter of days, or even hours.&lt;/p&gt;&#xA;&lt;p&gt;There can be a lot of value in creating a functional prototype of an idea so people can really test out the interactions and experience in ways that are not feasible with less interactive approaches.&lt;/p&gt;&#xA;&lt;p&gt;People like to push boundaries, so I imagine a future, maybe it&amp;rsquo;s already here, where people begin to ask: &lt;strong&gt;How do I ship my quickly created prototype in our product?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/vibe-code-feature-min.png&#34; alt=&#34;gru plan meme: vibe-code a feature; feature is cool; ship the feature (assertive); ship the feature (scared)&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Lots of people will tell you that you can&amp;rsquo;t just ship a vibe coded feature — well, maybe those &amp;ldquo;people&amp;rdquo; are developers. There are some good reasons why you should think twice about shipping a vibe coded concept:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Do you understand the code the agent wrote?&lt;/li&gt;&#xA;&lt;li&gt;What dependencies does the application use?&lt;/li&gt;&#xA;&lt;li&gt;How does it store data and is it secure?&lt;/li&gt;&#xA;&lt;li&gt;How does the login system work, if it has one?&lt;/li&gt;&#xA;&lt;li&gt;… the list goes on&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Honestly, the same questions should be asked no matter how the code was created. If the vibe coded prototype is the MVP, then you have many options, perhaps even just shipping the prototype as-is.&lt;/p&gt;&#xA;&lt;p&gt;Typically, we&amp;rsquo;re in a situation where we already have an existing platform or application. We can&amp;rsquo;t just ship the prototype. We need to somehow merge the prototype into the existing code base.&lt;/p&gt;&#xA;&lt;p&gt;So the more important question could be: &lt;strong&gt;How do we integrate this code into our existing platform or application?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Existing platforms and applications have data stores, UI frameworks, routing mechanisms, APIs, and other &amp;ldquo;primitives&amp;rdquo; used to create and evolve the systems.&lt;/p&gt;&#xA;&lt;p&gt;We have some options.&lt;/p&gt;&#xA;&lt;h1 id=&#34;breakdown-and-rebuild&#34;&gt;Breakdown and Rebuild&lt;/h1&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s treat the prototype as exactly that — a quickly created example of what we want to deliver. With this option we should audit the prototype and decompose it into the pieces used to create it.  Once we have a breakdown of the system, we can start re-composing the concept using our own primitives.&lt;/p&gt;&#xA;&lt;p&gt;This approach is what we&amp;rsquo;ve been doing for years for hastily created prototypes created during hackathons or discovery spikes. The process won&amp;rsquo;t feel any faster and certainly doesn&amp;rsquo;t feel &amp;ldquo;agentic&amp;rdquo;, but it works.&lt;/p&gt;&#xA;&lt;h1 id=&#34;vibe-code-with-our-primitives&#34;&gt;Vibe Code with Our Primitives &lt;/h1&gt;&#xA;&lt;p&gt;What if we could &amp;ldquo;teach&amp;rdquo; the vibe coding agent about the primitives we use?  It could use our UI framework and APIs. It could understand our authentication system. It could store data into our data stores.&lt;/p&gt;&#xA;&lt;p&gt;Agentic code assistants like Cursor and Copilot have &lt;a href=&#34;https://docs.cursor.com/context/rules&#34;&gt;rule&lt;/a&gt; or &lt;a href=&#34;https://docs.github.com/en/copilot/how-tos/custom-instructions/adding-repository-custom-instructions-for-github-copilot&#34;&gt;instruction&lt;/a&gt; files that can provide guidance and details for how the agent constructs the code, and can include code snippets and examples.&lt;/p&gt;&#xA;&lt;p&gt;Agents can also use MCP servers that can be used to provide more semantic content to the agent about the frameworks and systems already in place.&lt;/p&gt;&#xA;&lt;p&gt;This approach is how we create the most impact and productivity from  code assistant tools. We might not be at this point yet, but we keep getting closer.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Thoughts on AI Assisted Coding</title>
				<link>https://starkravingfinkle.org/posts/2025/07/thoughts-on-ai-assisted-coding/</link>
				<pubDate>Wed, 02 Jul 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/07/thoughts-on-ai-assisted-coding/</guid>
				<description>&lt;p&gt;I&amp;rsquo;ve been using Copilot in VS Code to help build several different projects. I&amp;rsquo;ve also used Claude, Gemini, and ChatGPT to help start and evolve projects. I find the tools to be very helpful and I feel more productive using them. I also find myself having more fun when using the tools.&lt;/p&gt;&#xA;&lt;p&gt;I have no doubt that these tools are here to stay and will become a standard part of software development.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-kinds-of-projects&#34;&gt;What Kinds of Projects&lt;/h2&gt;&#xA;&lt;p&gt;I frequently use code assistance when creating Python-based utility scripts for manipulating data or automating websites. Agents have a solid &amp;ldquo;understanding&amp;rdquo; of Python and JavaScript, so I rarely have issues with the generated code itself. I typically need to help with managing the code structure and &lt;a href=&#34;https://en.wikipedia.org/wiki/Modularity&#34;&gt;modularity&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;My children and hobbies will sometimes nudge me to create more full-blown web apps. I like to structure those projects as vanilla HTML &amp;amp; JavaScript frontends, with Python-based API backends.&lt;/p&gt;&#xA;&lt;p&gt;Agents will typically try to use a popular framework, but I push it towards modern JavaScript and Web Components.&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-approach&#34;&gt;My Approach&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Make Useful Prompts:&lt;/strong&gt; Start with an overview of the work you want to accomplish, and then kickoff the initial prompt with the basic starting point. I will make it clear that I want minimal dependencies, and specify any base frameworks we should use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Get the Right Context:&lt;/strong&gt; It&amp;rsquo;s easy for a code assistant to &amp;ldquo;forget&amp;rdquo; about existing code and recent changes. Use the context tools to make sure the code assistant is aware of relevant code, and be explicit about the context in that code. If you aren&amp;rsquo;t mindful of existing context, the code assistant is likely to create duplicate code, or not use existing code. Because I split up frontend and backend code, I will need to remind the code assistant about one or the other when refactoring. Agent-mode can have less issues in this situation.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Prepare for the Journey:&lt;/strong&gt; Break up the process into tasks/steps. Yes, this means you need to plan out the workflow and have a general idea of the system and architecture. I&amp;rsquo;ve seen people talk about using a one-shot prompt/agent to build an entire solution, but that has not been my experience. Perhaps a personal bias, but I want to control the development flow. I&amp;rsquo;m less interested in a one-shot agent building a complete solution.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Options and Suggestions:&lt;/strong&gt; Even though you have a general idea of the system and architecture, I frequently ask the code assistant for suggestions and options when starting subsequent steps. The code assistant may even recommend one of the suggestions over the others. This is where you need to think about the ramifications of the options. I occasionally veto all suggestions, but use the input to create a new prompt. Other times, I&amp;rsquo;ll use &lt;em&gt;parts&lt;/em&gt; of one of more suggestions, but explicitly define the new approach in the next prompt.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Be Subtractive:&lt;/strong&gt; Code assistant seem motivated to add code, even expanding beyond the scope of your prompt. Adding new capabilities to your code base can be exciting, but this is a siren song that leads to tech debt and code you no longer understand. I am the editor of this work and I trim and delete parts of the code I do not want at this point. I might want to add something in the future, but not now. One fascinating and frustrating behavior is the code assistant&amp;rsquo;s desire to always add back the code I edit away. We need to explicitly tell the code assistant we don&amp;rsquo;t want some parts of the code. Yes, some prompts are just refining the context for the model to avoid future indulgences.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Forest and Trees:&lt;/strong&gt; Most of the time I end up working with the code assistant on specific pieces and areas of code. Every now and then it&amp;rsquo;s useful to have the code assistant appraise larger views of the project. This can be easier with agents, but can also be done with context-providing tools too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;other-peoples-thoughts&#34;&gt;Other People&amp;rsquo;s Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;You might find some of these posts interesting and informative as well:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://taras.glek.net/posts/focus-and-context-and-llms/&#34;&gt;Focus and Context and LLMs&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://steipete.me/posts/2025/when-ai-meets-madness-peters-16-hour-days&#34;&gt;How Peter Builds Apps 20X Faster with AI&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.maxemitchell.com/writings/i-read-all-of-cloudflares-claude-generated-commits/&#34;&gt;I Read All of Cloudflare&amp;rsquo;s Claude-Generated Commits&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://blog.nilenso.com/blog/2025/05/29/ai-assisted-coding/&#34;&gt;AI Assisted Coding For Teams That Can&amp;rsquo;t Get Away With Vibes&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I&amp;rsquo;ve been using Copilot in VS Code to help build several different projects. I&amp;rsquo;ve also used Claude, Gemini, and ChatGPT to help start and evolve projects. I find the tools to be very helpful and I feel more productive using them. I also find myself having more fun when using the tools.&lt;/p&gt;&#xA;&lt;p&gt;I have no doubt that these tools are here to stay and will become a standard part of software development.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-kinds-of-projects&#34;&gt;What Kinds of Projects&lt;/h2&gt;&#xA;&lt;p&gt;I frequently use code assistance when creating Python-based utility scripts for manipulating data or automating websites. Agents have a solid &amp;ldquo;understanding&amp;rdquo; of Python and JavaScript, so I rarely have issues with the generated code itself. I typically need to help with managing the code structure and &lt;a href=&#34;https://en.wikipedia.org/wiki/Modularity&#34;&gt;modularity&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;My children and hobbies will sometimes nudge me to create more full-blown web apps. I like to structure those projects as vanilla HTML &amp;amp; JavaScript frontends, with Python-based API backends.&lt;/p&gt;&#xA;&lt;p&gt;Agents will typically try to use a popular framework, but I push it towards modern JavaScript and Web Components.&lt;/p&gt;&#xA;&lt;h2 id=&#34;my-approach&#34;&gt;My Approach&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Make Useful Prompts:&lt;/strong&gt; Start with an overview of the work you want to accomplish, and then kickoff the initial prompt with the basic starting point. I will make it clear that I want minimal dependencies, and specify any base frameworks we should use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Get the Right Context:&lt;/strong&gt; It&amp;rsquo;s easy for a code assistant to &amp;ldquo;forget&amp;rdquo; about existing code and recent changes. Use the context tools to make sure the code assistant is aware of relevant code, and be explicit about the context in that code. If you aren&amp;rsquo;t mindful of existing context, the code assistant is likely to create duplicate code, or not use existing code. Because I split up frontend and backend code, I will need to remind the code assistant about one or the other when refactoring. Agent-mode can have less issues in this situation.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Prepare for the Journey:&lt;/strong&gt; Break up the process into tasks/steps. Yes, this means you need to plan out the workflow and have a general idea of the system and architecture. I&amp;rsquo;ve seen people talk about using a one-shot prompt/agent to build an entire solution, but that has not been my experience. Perhaps a personal bias, but I want to control the development flow. I&amp;rsquo;m less interested in a one-shot agent building a complete solution.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Options and Suggestions:&lt;/strong&gt; Even though you have a general idea of the system and architecture, I frequently ask the code assistant for suggestions and options when starting subsequent steps. The code assistant may even recommend one of the suggestions over the others. This is where you need to think about the ramifications of the options. I occasionally veto all suggestions, but use the input to create a new prompt. Other times, I&amp;rsquo;ll use &lt;em&gt;parts&lt;/em&gt; of one of more suggestions, but explicitly define the new approach in the next prompt.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Be Subtractive:&lt;/strong&gt; Code assistant seem motivated to add code, even expanding beyond the scope of your prompt. Adding new capabilities to your code base can be exciting, but this is a siren song that leads to tech debt and code you no longer understand. I am the editor of this work and I trim and delete parts of the code I do not want at this point. I might want to add something in the future, but not now. One fascinating and frustrating behavior is the code assistant&amp;rsquo;s desire to always add back the code I edit away. We need to explicitly tell the code assistant we don&amp;rsquo;t want some parts of the code. Yes, some prompts are just refining the context for the model to avoid future indulgences.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Forest and Trees:&lt;/strong&gt; Most of the time I end up working with the code assistant on specific pieces and areas of code. Every now and then it&amp;rsquo;s useful to have the code assistant appraise larger views of the project. This can be easier with agents, but can also be done with context-providing tools too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;other-peoples-thoughts&#34;&gt;Other People&amp;rsquo;s Thoughts&lt;/h2&gt;&#xA;&lt;p&gt;You might find some of these posts interesting and informative as well:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://taras.glek.net/posts/focus-and-context-and-llms/&#34;&gt;Focus and Context and LLMs&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://steipete.me/posts/2025/when-ai-meets-madness-peters-16-hour-days&#34;&gt;How Peter Builds Apps 20X Faster with AI&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.maxemitchell.com/writings/i-read-all-of-cloudflares-claude-generated-commits/&#34;&gt;I Read All of Cloudflare&amp;rsquo;s Claude-Generated Commits&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://blog.nilenso.com/blog/2025/05/29/ai-assisted-coding/&#34;&gt;AI Assisted Coding For Teams That Can&amp;rsquo;t Get Away With Vibes&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Building Agents: Creating JSONLogic from Intent</title>
				<link>https://starkravingfinkle.org/posts/2025/04/building-agents-creating-jsonlogic-from-intent/</link>
				<pubDate>Sun, 27 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/04/building-agents-creating-jsonlogic-from-intent/</guid>
				<description>&lt;p&gt;I had a lot of fun working through my &amp;ldquo;Exploring LLMs as Agent&amp;rdquo; series. I&amp;rsquo;m starting to dive a little deeper into some specific Agent use cases, so I&amp;rsquo;ll move away from &amp;ldquo;exploring&amp;rdquo; and start &amp;ldquo;building&amp;rdquo; agents.&lt;/p&gt;&#xA;&lt;p&gt;At work, we&amp;rsquo;ve started configuring rules outside of code, especially for situations where a non-developer wants to create some rule-based logic. We&amp;rsquo;ve looked at &lt;a href=&#34;https://jsonlogic.com/&#34;&gt;JSONLogic&lt;/a&gt; for this use case, but there are other approaches too.&lt;/p&gt;&#xA;&lt;p&gt;Rule logic can get complex, which makes it hard for those non-developers (and even developers) to create valid rules. Maybe this is a use case for an Agent?&lt;/p&gt;&#xA;&lt;p&gt;TLDR: Here’s the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents/tree/main/jsonlogic_agent&#34;&gt;code repo&lt;/a&gt; for the JSONLogic Agent. It&amp;rsquo;s part of my ADK &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents/tree/main&#34;&gt;code repo&lt;/a&gt;. Keeping the ADK Agents in the same repo seemed like a good idea.&lt;/p&gt;&#xA;&lt;h2 id=&#34;jsonlogic-agent&#34;&gt;JSONLogic Agent&lt;/h2&gt;&#xA;&lt;p&gt;I decided to build an Agent, based on Google&amp;rsquo;s &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; (ADK) since I had some &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-google-agent-dev-kit/&#34;&gt;experience&lt;/a&gt; with the framework. I had a rough idea of what I thought I wanted to do:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create a schema for JSONLogic&lt;/li&gt;&#xA;&lt;li&gt;Create a schema for the dataset (JSON) being evaluated&lt;/li&gt;&#xA;&lt;li&gt;Expose the schemas to the Agent, with a sprinkle of ReAct planning&lt;/li&gt;&#xA;&lt;li&gt;Agent create JSONLogic on its own&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;side-track&#34;&gt;Side Track&lt;/h3&gt;&#xA;&lt;p&gt;Something I&amp;rsquo;ve started doing recently is asking Claude or Gemini what they think of the approach, and if they have any other options or suggestions. In this case, Claude suggested having the Agent create an &lt;em&gt;intermediate format&lt;/em&gt;, not actual JSONLogic, and then using a separate tool to convert from the intermediate format to JSONLogic.&lt;/p&gt;&#xA;&lt;p&gt;I didn&amp;rsquo;t much care for that idea, but I ended up doing just that. Initial experiments showed that the Agent didn&amp;rsquo;t always create valid or even functional JSONLogic — which doesn&amp;rsquo;t really have a well defined schema. Now, those problems could have been my own failings, but I began to see opportunities having the Agent &lt;em&gt;parse my intent of the rule&lt;/em&gt;, using a higher-level, simpler structure. So the plan became:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create a schema for the Parsed Intent format&lt;/li&gt;&#xA;&lt;li&gt;Create a schema for the dataset (JSON) being evaluated&lt;/li&gt;&#xA;&lt;li&gt;Expose the schemas to the Agent, with a sprinkle of ReAct planning&lt;/li&gt;&#xA;&lt;li&gt;Expose a tool to convert Parsed Intent to JSONLogic&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;parsing-intent&#34;&gt;Parsing Intent&lt;/h2&gt;&#xA;&lt;p&gt;Instead of trying to make sure the Agent knew all the syntax and nuance around JSONLogic, I created a simple format of &lt;code&gt;operations&lt;/code&gt; and &lt;code&gt;conditions&lt;/code&gt; (which can be nested) to capture the user&amp;rsquo;s intent of the rule. It looks like this (from the system prompt):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;The intent should follow this format:&#xA;{&#xA;  &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;  &amp;#34;conditions&amp;#34;: [&#xA;    {&#xA;      &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;      &amp;#34;operator&amp;#34;: &amp;#34;One of the operators above&amp;#34;,&#xA;      &amp;#34;value&amp;#34;: &amp;#34;The value to compare against (for most operators)&amp;#34;&#xA;    },&#xA;    // Additional conditions...&#xA;  ]&#xA;}&#xA;&#xA;For nested conditions, use this structure:&#xA;{&#xA;  &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;  &amp;#34;conditions&amp;#34;: [&#xA;    {&#xA;      &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;      &amp;#34;operator&amp;#34;: &amp;#34;operators from above&amp;#34;,&#xA;      &amp;#34;value&amp;#34;: &amp;#34;the value&amp;#34;&#xA;    },&#xA;    {&#xA;      &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;      &amp;#34;conditions&amp;#34;: [&#xA;        {&#xA;          &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;          &amp;#34;operator&amp;#34;: &amp;#34;operators from above&amp;#34;, &#xA;          &amp;#34;value&amp;#34;: &amp;#34;the value&amp;#34;&#xA;        },&#xA;        // Additional nested conditions...&#xA;      ]&#xA;    },&#xA;    // Additional conditions...&#xA;  ]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One of the interesting aspects of using this approach is that I could create high-level operations that are not natively supported in JSONLogic directly, but could be implemented using JSONLogic — like macros. Here are some, again pulling from the system prompt:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;When parsing conditions for the intent, use ONLY these operator keywords:&#xA;&#xA;- **Comparison Operators**:&#xA;  - Basic: &amp;#34;equals&amp;#34;, &amp;#34;notEquals&amp;#34;, &amp;#34;greaterThan&amp;#34;, &amp;#34;lessThan&amp;#34;, ...&#xA;  - Range: &amp;#34;between&amp;#34;, &amp;#34;betweenOrEqual&amp;#34;&#xA;  &#xA;- **Logical Operators**:&#xA;  - &amp;#34;AND&amp;#34;, &amp;#34;OR&amp;#34;, &amp;#34;NOT&amp;#34;&#xA;  &#xA;- **String Operators**:&#xA;  - &amp;#34;startsWith&amp;#34;, &amp;#34;endsWith&amp;#34;, &amp;#34;contains&amp;#34; (contains substring)&#xA;  &#xA;- **Array Operators**:&#xA;  - Empty checks: &amp;#34;empty&amp;#34;, &amp;#34;notEmpty&amp;#34;&#xA;  - Item checks: &amp;#34;contains&amp;#34; (is value or values in array)&#xA;  - Index access: use dot notation to access array elements, e.g., &amp;#34;object.array.0&amp;#34;&#xA;  - Length checks: &amp;#34;lengthEquals&amp;#34;, &amp;#34;lengthGreaterThan&amp;#34;, &amp;#34;lengthLessThan&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some of these operations are directly supported by JSONLogic. Others are supported if you understand the &amp;ldquo;&lt;a href=&#34;https://jsonlogic.com/truthy.html&#34;&gt;Truthy &amp;amp; Falsy&lt;/a&gt;&amp;rdquo; nuance JSONLogic applies. The &lt;strong&gt;Range&lt;/strong&gt; checks are a special case that JSONLogic supports, but using &amp;ldquo;between&amp;rdquo; is much more explicit. Item checks for a single item in an array are directly supported, but not checks with multiple items in an array — but that can be implemented using &lt;code&gt;some&lt;/code&gt;. &lt;strong&gt;Length&lt;/strong&gt; checks are not directly supported, but can be implemented using &lt;code&gt;reduce&lt;/code&gt;, an idea I stole from a co-worker.&lt;/p&gt;&#xA;&lt;p&gt;Checkout &lt;code&gt;create_jsonlogic_from_intent&lt;/code&gt; in &lt;code&gt;[jsonlogic.py](https://github.com/mfinkle/google-adk-agents/blob/main/jsonlogic_agent/tools/jsonlogic.py)&lt;/code&gt; to see how I hacked some of these &amp;ldquo;macro&amp;rdquo; operations together.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;The JSONLogic Agent feels like a success. I include two different test datasets and a series of predefined &amp;ldquo;questions&amp;rdquo; for the Agent to act on and output JSONLogic. The command line tool will even execute the JSONLogic against the same datasets and compare to an expected answer. Here&amp;rsquo;s an example:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Query: Find customers who completed the survey in less than 5 minutes (300 seconds) and reported at least one pain point&#xA;&#xA;Planning:&#xA;1. Get the available fields from the schema to identify the correct field names for &amp;#34;time spent&amp;#34; and &amp;#34;pain points&amp;#34;.&#xA;2. Parse the user&amp;#39;s intent into a structured representation. This will involve identifying the conditions for &amp;#34;time spent&amp;#34; being less than 300 seconds and &amp;#34;pain points&amp;#34; array not being empty.&#xA;3. Convert the structured intent into a JSONLogic rule using the `create_jsonlogic_from_intent` tool.&#xA;4. Validate the fields in the JSONLogic rule against the available fields from the schema using the `validate_fields_from_jsonlogic` tool.&#xA;5. Return the complete JSONLogic rule.&#xA;&#xA;The relevant fields are:&#xA;- metadata.timeSpent (time spent)&#xA;- questions.satisfaction.painPoints.response (pain points)&#xA;&#xA;Parsed Intent:&#xA;{&#xA; &amp;#34;operation&amp;#34;: &amp;#34;AND&amp;#34;,&#xA; &amp;#34;conditions&amp;#34;: [&#xA;   {&#xA;     &amp;#34;operator&amp;#34;: &amp;#34;lessThan&amp;#34;,&#xA;     &amp;#34;field&amp;#34;: &amp;#34;metadata.timeSpent&amp;#34;,&#xA;     &amp;#34;value&amp;#34;: 300&#xA;   },&#xA;   {&#xA;     &amp;#34;field&amp;#34;: &amp;#34;questions.satisfaction.painPoints.response&amp;#34;,&#xA;     &amp;#34;operator&amp;#34;: &amp;#34;notEmpty&amp;#34;&#xA;   }&#xA; ]&#xA;}&#xA;&#xA;Generated JSONLogic rule:&#xA;{&#xA; &amp;#34;and&amp;#34;: [&#xA;   {&#xA;     &amp;#34;&amp;lt;&amp;#34;: [&#xA;       {&#xA;         &amp;#34;var&amp;#34;: &amp;#34;metadata.timeSpent&amp;#34;&#xA;       },&#xA;       300&#xA;     ]&#xA;   },&#xA;   {&#xA;     &amp;#34;!=&amp;#34;: [&#xA;       {&#xA;         &amp;#34;var&amp;#34;: &amp;#34;questions.satisfaction.painPoints.response&amp;#34;&#xA;       },&#xA;       []&#xA;     ]&#xA;   }&#xA; ]&#xA;}&#xA;&#xA;Test result against sample data: False&#xA;? PASS - Result matches expected outcome: False&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I added some utilities to make it a little easier to work with ADK Agents from mechanisms other than the &lt;code&gt;adk&lt;/code&gt; command line tools.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll keep building some Agents using ADK. I want to explore vector search and embeddings, and how those might be used together with an Agent.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I had a lot of fun working through my &amp;ldquo;Exploring LLMs as Agent&amp;rdquo; series. I&amp;rsquo;m starting to dive a little deeper into some specific Agent use cases, so I&amp;rsquo;ll move away from &amp;ldquo;exploring&amp;rdquo; and start &amp;ldquo;building&amp;rdquo; agents.&lt;/p&gt;&#xA;&lt;p&gt;At work, we&amp;rsquo;ve started configuring rules outside of code, especially for situations where a non-developer wants to create some rule-based logic. We&amp;rsquo;ve looked at &lt;a href=&#34;https://jsonlogic.com/&#34;&gt;JSONLogic&lt;/a&gt; for this use case, but there are other approaches too.&lt;/p&gt;&#xA;&lt;p&gt;Rule logic can get complex, which makes it hard for those non-developers (and even developers) to create valid rules. Maybe this is a use case for an Agent?&lt;/p&gt;&#xA;&lt;p&gt;TLDR: Here’s the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents/tree/main/jsonlogic_agent&#34;&gt;code repo&lt;/a&gt; for the JSONLogic Agent. It&amp;rsquo;s part of my ADK &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents/tree/main&#34;&gt;code repo&lt;/a&gt;. Keeping the ADK Agents in the same repo seemed like a good idea.&lt;/p&gt;&#xA;&lt;h2 id=&#34;jsonlogic-agent&#34;&gt;JSONLogic Agent&lt;/h2&gt;&#xA;&lt;p&gt;I decided to build an Agent, based on Google&amp;rsquo;s &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; (ADK) since I had some &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-google-agent-dev-kit/&#34;&gt;experience&lt;/a&gt; with the framework. I had a rough idea of what I thought I wanted to do:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create a schema for JSONLogic&lt;/li&gt;&#xA;&lt;li&gt;Create a schema for the dataset (JSON) being evaluated&lt;/li&gt;&#xA;&lt;li&gt;Expose the schemas to the Agent, with a sprinkle of ReAct planning&lt;/li&gt;&#xA;&lt;li&gt;Agent create JSONLogic on its own&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;side-track&#34;&gt;Side Track&lt;/h3&gt;&#xA;&lt;p&gt;Something I&amp;rsquo;ve started doing recently is asking Claude or Gemini what they think of the approach, and if they have any other options or suggestions. In this case, Claude suggested having the Agent create an &lt;em&gt;intermediate format&lt;/em&gt;, not actual JSONLogic, and then using a separate tool to convert from the intermediate format to JSONLogic.&lt;/p&gt;&#xA;&lt;p&gt;I didn&amp;rsquo;t much care for that idea, but I ended up doing just that. Initial experiments showed that the Agent didn&amp;rsquo;t always create valid or even functional JSONLogic — which doesn&amp;rsquo;t really have a well defined schema. Now, those problems could have been my own failings, but I began to see opportunities having the Agent &lt;em&gt;parse my intent of the rule&lt;/em&gt;, using a higher-level, simpler structure. So the plan became:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create a schema for the Parsed Intent format&lt;/li&gt;&#xA;&lt;li&gt;Create a schema for the dataset (JSON) being evaluated&lt;/li&gt;&#xA;&lt;li&gt;Expose the schemas to the Agent, with a sprinkle of ReAct planning&lt;/li&gt;&#xA;&lt;li&gt;Expose a tool to convert Parsed Intent to JSONLogic&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;parsing-intent&#34;&gt;Parsing Intent&lt;/h2&gt;&#xA;&lt;p&gt;Instead of trying to make sure the Agent knew all the syntax and nuance around JSONLogic, I created a simple format of &lt;code&gt;operations&lt;/code&gt; and &lt;code&gt;conditions&lt;/code&gt; (which can be nested) to capture the user&amp;rsquo;s intent of the rule. It looks like this (from the system prompt):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;The intent should follow this format:&#xA;{&#xA;  &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;  &amp;#34;conditions&amp;#34;: [&#xA;    {&#xA;      &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;      &amp;#34;operator&amp;#34;: &amp;#34;One of the operators above&amp;#34;,&#xA;      &amp;#34;value&amp;#34;: &amp;#34;The value to compare against (for most operators)&amp;#34;&#xA;    },&#xA;    // Additional conditions...&#xA;  ]&#xA;}&#xA;&#xA;For nested conditions, use this structure:&#xA;{&#xA;  &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;  &amp;#34;conditions&amp;#34;: [&#xA;    {&#xA;      &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;      &amp;#34;operator&amp;#34;: &amp;#34;operators from above&amp;#34;,&#xA;      &amp;#34;value&amp;#34;: &amp;#34;the value&amp;#34;&#xA;    },&#xA;    {&#xA;      &amp;#34;operation&amp;#34;: &amp;#34;logical operation (AND, OR, NOT)&amp;#34;,&#xA;      &amp;#34;conditions&amp;#34;: [&#xA;        {&#xA;          &amp;#34;field&amp;#34;: &amp;#34;data field&amp;#34;,&#xA;          &amp;#34;operator&amp;#34;: &amp;#34;operators from above&amp;#34;, &#xA;          &amp;#34;value&amp;#34;: &amp;#34;the value&amp;#34;&#xA;        },&#xA;        // Additional nested conditions...&#xA;      ]&#xA;    },&#xA;    // Additional conditions...&#xA;  ]&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;One of the interesting aspects of using this approach is that I could create high-level operations that are not natively supported in JSONLogic directly, but could be implemented using JSONLogic — like macros. Here are some, again pulling from the system prompt:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;When parsing conditions for the intent, use ONLY these operator keywords:&#xA;&#xA;- **Comparison Operators**:&#xA;  - Basic: &amp;#34;equals&amp;#34;, &amp;#34;notEquals&amp;#34;, &amp;#34;greaterThan&amp;#34;, &amp;#34;lessThan&amp;#34;, ...&#xA;  - Range: &amp;#34;between&amp;#34;, &amp;#34;betweenOrEqual&amp;#34;&#xA;  &#xA;- **Logical Operators**:&#xA;  - &amp;#34;AND&amp;#34;, &amp;#34;OR&amp;#34;, &amp;#34;NOT&amp;#34;&#xA;  &#xA;- **String Operators**:&#xA;  - &amp;#34;startsWith&amp;#34;, &amp;#34;endsWith&amp;#34;, &amp;#34;contains&amp;#34; (contains substring)&#xA;  &#xA;- **Array Operators**:&#xA;  - Empty checks: &amp;#34;empty&amp;#34;, &amp;#34;notEmpty&amp;#34;&#xA;  - Item checks: &amp;#34;contains&amp;#34; (is value or values in array)&#xA;  - Index access: use dot notation to access array elements, e.g., &amp;#34;object.array.0&amp;#34;&#xA;  - Length checks: &amp;#34;lengthEquals&amp;#34;, &amp;#34;lengthGreaterThan&amp;#34;, &amp;#34;lengthLessThan&amp;#34;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some of these operations are directly supported by JSONLogic. Others are supported if you understand the &amp;ldquo;&lt;a href=&#34;https://jsonlogic.com/truthy.html&#34;&gt;Truthy &amp;amp; Falsy&lt;/a&gt;&amp;rdquo; nuance JSONLogic applies. The &lt;strong&gt;Range&lt;/strong&gt; checks are a special case that JSONLogic supports, but using &amp;ldquo;between&amp;rdquo; is much more explicit. Item checks for a single item in an array are directly supported, but not checks with multiple items in an array — but that can be implemented using &lt;code&gt;some&lt;/code&gt;. &lt;strong&gt;Length&lt;/strong&gt; checks are not directly supported, but can be implemented using &lt;code&gt;reduce&lt;/code&gt;, an idea I stole from a co-worker.&lt;/p&gt;&#xA;&lt;p&gt;Checkout &lt;code&gt;create_jsonlogic_from_intent&lt;/code&gt; in &lt;code&gt;[jsonlogic.py](https://github.com/mfinkle/google-adk-agents/blob/main/jsonlogic_agent/tools/jsonlogic.py)&lt;/code&gt; to see how I hacked some of these &amp;ldquo;macro&amp;rdquo; operations together.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;The JSONLogic Agent feels like a success. I include two different test datasets and a series of predefined &amp;ldquo;questions&amp;rdquo; for the Agent to act on and output JSONLogic. The command line tool will even execute the JSONLogic against the same datasets and compare to an expected answer. Here&amp;rsquo;s an example:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Query: Find customers who completed the survey in less than 5 minutes (300 seconds) and reported at least one pain point&#xA;&#xA;Planning:&#xA;1. Get the available fields from the schema to identify the correct field names for &amp;#34;time spent&amp;#34; and &amp;#34;pain points&amp;#34;.&#xA;2. Parse the user&amp;#39;s intent into a structured representation. This will involve identifying the conditions for &amp;#34;time spent&amp;#34; being less than 300 seconds and &amp;#34;pain points&amp;#34; array not being empty.&#xA;3. Convert the structured intent into a JSONLogic rule using the `create_jsonlogic_from_intent` tool.&#xA;4. Validate the fields in the JSONLogic rule against the available fields from the schema using the `validate_fields_from_jsonlogic` tool.&#xA;5. Return the complete JSONLogic rule.&#xA;&#xA;The relevant fields are:&#xA;- metadata.timeSpent (time spent)&#xA;- questions.satisfaction.painPoints.response (pain points)&#xA;&#xA;Parsed Intent:&#xA;{&#xA; &amp;#34;operation&amp;#34;: &amp;#34;AND&amp;#34;,&#xA; &amp;#34;conditions&amp;#34;: [&#xA;   {&#xA;     &amp;#34;operator&amp;#34;: &amp;#34;lessThan&amp;#34;,&#xA;     &amp;#34;field&amp;#34;: &amp;#34;metadata.timeSpent&amp;#34;,&#xA;     &amp;#34;value&amp;#34;: 300&#xA;   },&#xA;   {&#xA;     &amp;#34;field&amp;#34;: &amp;#34;questions.satisfaction.painPoints.response&amp;#34;,&#xA;     &amp;#34;operator&amp;#34;: &amp;#34;notEmpty&amp;#34;&#xA;   }&#xA; ]&#xA;}&#xA;&#xA;Generated JSONLogic rule:&#xA;{&#xA; &amp;#34;and&amp;#34;: [&#xA;   {&#xA;     &amp;#34;&amp;lt;&amp;#34;: [&#xA;       {&#xA;         &amp;#34;var&amp;#34;: &amp;#34;metadata.timeSpent&amp;#34;&#xA;       },&#xA;       300&#xA;     ]&#xA;   },&#xA;   {&#xA;     &amp;#34;!=&amp;#34;: [&#xA;       {&#xA;         &amp;#34;var&amp;#34;: &amp;#34;questions.satisfaction.painPoints.response&amp;#34;&#xA;       },&#xA;       []&#xA;     ]&#xA;   }&#xA; ]&#xA;}&#xA;&#xA;Test result against sample data: False&#xA;? PASS - Result matches expected outcome: False&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I added some utilities to make it a little easier to work with ADK Agents from mechanisms other than the &lt;code&gt;adk&lt;/code&gt; command line tools.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I&amp;rsquo;ll keep building some Agents using ADK. I want to explore vector search and embeddings, and how those might be used together with an Agent.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Google Agent Dev Kit</title>
				<link>https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-google-agent-dev-kit/</link>
				<pubDate>Mon, 14 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-google-agent-dev-kit/</guid>
				<description>&lt;p&gt;Up to this point, I have been experimenting with my own home-grown agent framework, based on Simon Willison&amp;rsquo;s great &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;LLM&lt;/a&gt; project to handle wrapping the different LLMs. Armed with some experience, I want to start looking at real frameworks. Google released an &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; at Google Next. It supports many of the features I have been playing with, including tool calling, planning, MCP, and local models. It also supports some advanced concepts like agent orchestration.&lt;/p&gt;&#xA;&lt;p&gt;TLDR: Here&amp;rsquo;s the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents&#34;&gt;code repo&lt;/a&gt; for my initial ADK exploration.&lt;/p&gt;&#xA;&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s ADK has many features that are becoming common in agent frameworks:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Agent with support for multiple LLMs, including local using &lt;a href=&#34;https://www.litellm.ai/&#34;&gt;LiteLLM&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Tools that support custom methods, a set of built-ins, 3rd party wrappers (including MCP), and even an easy AgentTool&lt;/li&gt;&#xA;&lt;li&gt;Session and Memory capabilities with in-memory and persistent examples&lt;/li&gt;&#xA;&lt;li&gt;Orchestration with different types of Agents to handle parallel, loops, and sequential workflows&lt;/li&gt;&#xA;&lt;li&gt;Built-in system of Evals for testing your Agents&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;ADK has a command line interface that is somewhat unique to agent frameworks. It reminds me of the Flask command line system:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;adk run &amp;lt;agent&amp;gt;&#xA;adk web&#xA;adk api_server&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ADK also supports a mechanism to deploy your agents to Google infrastructure (Agent Engine or Cloud Run). I&amp;rsquo;m hoping this could be used to deploy to other infrastructure too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;converting-toolagent&#34;&gt;Converting ToolAgent&lt;/h2&gt;&#xA;&lt;p&gt;I wanted to see how easily I could convert my &lt;code&gt;ToolAgent&lt;/code&gt; to use ADK. The core Agent was fairly small and simple:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root_agent = Agent(&#xA;    name=&amp;#34;multi_tool_agent&amp;#34;,&#xA;    model=&amp;#34;gemini-2.0-flash&amp;#34;,&#xA;    instruction=SYSTEM_PROMPT,&#xA;    generate_content_config=types.GenerateContentConfig(&#xA;        temperature=0 # More deterministic output&#xA;    ),&#xA;    planner=PlanReActPlanner(),&#xA;    tools=[&#xA;        FunctionTool(func=get_weather),&#xA;        FunctionTool(func=get_datetime),&#xA;        FunctionTool(func=get_current_location),&#xA;        FunctionTool(func=get_zipcode),&#xA;        FunctionTool(func=calculate),&#xA;        FunctionTool(func=get_appointment_specialties),&#xA;        FunctionTool(func=get_available_appointments),&#xA;        FunctionTool(func=get_appointment_details),&#xA;        FunctionTool(func=book_appointment),&#xA;        FunctionTool(func=cancel_appointment),&#xA;    ],&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code uses a much simpler system prompt because much of the &amp;ldquo;PLEASE USE TOOLS&amp;rdquo; prompt is handled by the tool calling system. I use a ReAct planner to get the &amp;ldquo;thinking&amp;rdquo; aspects I had in &lt;code&gt;ToolAgent&lt;/code&gt;. The tools themselves are still simple, mocked Python methods. ADK, like other frameworks, uses Python Typing and Docstrings to extract the specification details for the tool methods, so I changed my code (for the better):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;def get_available_appointments(specialty: str) -&amp;gt; list[dict]:&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;    Gets the available appointments for the given specialty.&#xA;    Args:&#xA;      specialty (str): the specialty name&#xA;    Returns:&#xA;      list: a list of available appointments&#xA;    Example:&#xA;        &amp;gt;&amp;gt;&amp;gt; get_available_appointments(&amp;#39;dentist&amp;#39;)&#xA;        [{&amp;#39;id&amp;#39;: &amp;#39;1&amp;#39;, &amp;#39;date&amp;#39;: &amp;#39;2022-01-01&amp;#39;, &amp;#39;time&amp;#39;: &amp;#39;10:00 AM&amp;#39;}, ...]&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;    &amp;lt;IMPL REMOVED&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s a simple exchange that required a few tools:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user: what dentist appointments are available tomorrow?&#xA;&#xA;[multi_tool_agent]: /*PLANNING*/&#xA;1. Get the current date using the `get_datetime` tool.&#xA;2. Extract the date from the response and calculate tomorrow&amp;#39;s date.&#xA;3. Get the available dentist appointments using the `get_available_appointments` tool with the specialty set to &amp;#34;dentist&amp;#34;.&#xA;4. Filter the appointments to only include those that are on tomorrow&amp;#39;s date.&#xA;5. Respond to the user with the available appointments for tomorrow.&#xA;&#xA;/*ACTION*/&#xA;&#xA;[multi_tool_agent]: /*REASONING*/&#xA;1. The current date is 2025-04-13.&#xA;2. Tomorrow&amp;#39;s date is 2025-04-14.&#xA;3. Now I need to find the available dentist appointments.&#xA;&#xA;/*ACTION*/&#xA;&#xA;[multi_tool_agent]: /*REASONING*/&#xA;I have the available dentist appointments. I need to filter the appointments to only include those that are on tomorrow&amp;#39;s date, which is 2025-04-14.&#xA;The available appointments for tomorrow are:&#xA;- id: 3, time: 11:00 AM&#xA;- id: 4, time: 3:00 PM&#xA;&#xA;/*FINAL_ANSWER*/&#xA;The available dentist appointments for tomorrow, 2025-04-14, are:&#xA;- id: 3, time: 11:00 AM&#xA;- id: 4, time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Overall, I thought it was pretty easy to get a basic agent working. Maybe I&amp;rsquo;ll try to create a &lt;code&gt;WebAgent&lt;/code&gt; too. I&amp;rsquo;ll keep exploring some of the more advanced concepts to see how those can be used.&lt;/p&gt;&#xA;&lt;p&gt;Checkout the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents&#34;&gt;code repo&lt;/a&gt;&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Up to this point, I have been experimenting with my own home-grown agent framework, based on Simon Willison&amp;rsquo;s great &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;LLM&lt;/a&gt; project to handle wrapping the different LLMs. Armed with some experience, I want to start looking at real frameworks. Google released an &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; at Google Next. It supports many of the features I have been playing with, including tool calling, planning, MCP, and local models. It also supports some advanced concepts like agent orchestration.&lt;/p&gt;&#xA;&lt;p&gt;TLDR: Here&amp;rsquo;s the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents&#34;&gt;code repo&lt;/a&gt; for my initial ADK exploration.&lt;/p&gt;&#xA;&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;&#xA;&lt;p&gt;Google&amp;rsquo;s ADK has many features that are becoming common in agent frameworks:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Agent with support for multiple LLMs, including local using &lt;a href=&#34;https://www.litellm.ai/&#34;&gt;LiteLLM&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Tools that support custom methods, a set of built-ins, 3rd party wrappers (including MCP), and even an easy AgentTool&lt;/li&gt;&#xA;&lt;li&gt;Session and Memory capabilities with in-memory and persistent examples&lt;/li&gt;&#xA;&lt;li&gt;Orchestration with different types of Agents to handle parallel, loops, and sequential workflows&lt;/li&gt;&#xA;&lt;li&gt;Built-in system of Evals for testing your Agents&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;ADK has a command line interface that is somewhat unique to agent frameworks. It reminds me of the Flask command line system:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;adk run &amp;lt;agent&amp;gt;&#xA;adk web&#xA;adk api_server&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;ADK also supports a mechanism to deploy your agents to Google infrastructure (Agent Engine or Cloud Run). I&amp;rsquo;m hoping this could be used to deploy to other infrastructure too.&lt;/p&gt;&#xA;&lt;h2 id=&#34;converting-toolagent&#34;&gt;Converting ToolAgent&lt;/h2&gt;&#xA;&lt;p&gt;I wanted to see how easily I could convert my &lt;code&gt;ToolAgent&lt;/code&gt; to use ADK. The core Agent was fairly small and simple:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;root_agent = Agent(&#xA;    name=&amp;#34;multi_tool_agent&amp;#34;,&#xA;    model=&amp;#34;gemini-2.0-flash&amp;#34;,&#xA;    instruction=SYSTEM_PROMPT,&#xA;    generate_content_config=types.GenerateContentConfig(&#xA;        temperature=0 # More deterministic output&#xA;    ),&#xA;    planner=PlanReActPlanner(),&#xA;    tools=[&#xA;        FunctionTool(func=get_weather),&#xA;        FunctionTool(func=get_datetime),&#xA;        FunctionTool(func=get_current_location),&#xA;        FunctionTool(func=get_zipcode),&#xA;        FunctionTool(func=calculate),&#xA;        FunctionTool(func=get_appointment_specialties),&#xA;        FunctionTool(func=get_available_appointments),&#xA;        FunctionTool(func=get_appointment_details),&#xA;        FunctionTool(func=book_appointment),&#xA;        FunctionTool(func=cancel_appointment),&#xA;    ],&#xA;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This code uses a much simpler system prompt because much of the &amp;ldquo;PLEASE USE TOOLS&amp;rdquo; prompt is handled by the tool calling system. I use a ReAct planner to get the &amp;ldquo;thinking&amp;rdquo; aspects I had in &lt;code&gt;ToolAgent&lt;/code&gt;. The tools themselves are still simple, mocked Python methods. ADK, like other frameworks, uses Python Typing and Docstrings to extract the specification details for the tool methods, so I changed my code (for the better):&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;def get_available_appointments(specialty: str) -&amp;gt; list[dict]:&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;    Gets the available appointments for the given specialty.&#xA;    Args:&#xA;      specialty (str): the specialty name&#xA;    Returns:&#xA;      list: a list of available appointments&#xA;    Example:&#xA;        &amp;gt;&amp;gt;&amp;gt; get_available_appointments(&amp;#39;dentist&amp;#39;)&#xA;        [{&amp;#39;id&amp;#39;: &amp;#39;1&amp;#39;, &amp;#39;date&amp;#39;: &amp;#39;2022-01-01&amp;#39;, &amp;#39;time&amp;#39;: &amp;#39;10:00 AM&amp;#39;}, ...]&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;    &amp;lt;IMPL REMOVED&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s a simple exchange that required a few tools:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;user: what dentist appointments are available tomorrow?&#xA;&#xA;[multi_tool_agent]: /*PLANNING*/&#xA;1. Get the current date using the `get_datetime` tool.&#xA;2. Extract the date from the response and calculate tomorrow&amp;#39;s date.&#xA;3. Get the available dentist appointments using the `get_available_appointments` tool with the specialty set to &amp;#34;dentist&amp;#34;.&#xA;4. Filter the appointments to only include those that are on tomorrow&amp;#39;s date.&#xA;5. Respond to the user with the available appointments for tomorrow.&#xA;&#xA;/*ACTION*/&#xA;&#xA;[multi_tool_agent]: /*REASONING*/&#xA;1. The current date is 2025-04-13.&#xA;2. Tomorrow&amp;#39;s date is 2025-04-14.&#xA;3. Now I need to find the available dentist appointments.&#xA;&#xA;/*ACTION*/&#xA;&#xA;[multi_tool_agent]: /*REASONING*/&#xA;I have the available dentist appointments. I need to filter the appointments to only include those that are on tomorrow&amp;#39;s date, which is 2025-04-14.&#xA;The available appointments for tomorrow are:&#xA;- id: 3, time: 11:00 AM&#xA;- id: 4, time: 3:00 PM&#xA;&#xA;/*FINAL_ANSWER*/&#xA;The available dentist appointments for tomorrow, 2025-04-14, are:&#xA;- id: 3, time: 11:00 AM&#xA;- id: 4, time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Overall, I thought it was pretty easy to get a basic agent working. Maybe I&amp;rsquo;ll try to create a &lt;code&gt;WebAgent&lt;/code&gt; too. I&amp;rsquo;ll keep exploring some of the more advanced concepts to see how those can be used.&lt;/p&gt;&#xA;&lt;p&gt;Checkout the &lt;a href=&#34;https://github.com/mfinkle/google-adk-agents&#34;&gt;code repo&lt;/a&gt;&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Local Models</title>
				<link>https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-local-models/</link>
				<pubDate>Sun, 13 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/04/exploring-llms-as-agents-local-models/</guid>
				<description>&lt;p&gt;I finally got around to exploring local models, which is surprisingly simple to set up. I wanted to see how well a local model would perform in &lt;code&gt;ToolAgent&lt;/code&gt; compared to the remote models (mostly Gemini) I have been using.&lt;/p&gt;&#xA;&lt;p&gt;For more context on my explorations , checkout the previous posts: &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/&#34;&gt;WebAgent Gets a Refactor&lt;/a&gt;, and &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-model-context-protocol/&#34;&gt;Model Context Protocol (MCP)&lt;/a&gt;. Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;local-models&#34;&gt;Local Models&lt;/h2&gt;&#xA;&lt;p&gt;Local large language models (LLMs) are models that can be run directly on a personal device, such as a laptop, desktop computer, or smartphone, rather than relying on cloud-based servers. They offer features like privacy, security, compliance, offline availability, and lower operating costs.&lt;/p&gt;&#xA;&lt;p&gt;One of the easiest ways to get started with local models is with &lt;a href=&#34;https://ollama.com/&#34;&gt;Ollama&lt;/a&gt;, a system to download, manage, and run models on your laptop. Another frequently used resource is &lt;a href=&#34;https://huggingface.co/&#34;&gt;Hugging Face&lt;/a&gt;, which I&amp;rsquo;ve heard called the &amp;ldquo;GitHub of Models&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;My &lt;code&gt;ToolAgent&lt;/code&gt; is based on &lt;a href=&#34;https://simonwillison.net/&#34;&gt;Simon Willison&amp;rsquo;s&lt;/a&gt; &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;&lt;code&gt;llm&lt;/code&gt; project,&lt;/a&gt; which has &lt;a href=&#34;https://llm.datasette.io/en/stable/plugins/directory.html#local-models&#34;&gt;plugins&lt;/a&gt; for several different local model system, including Ollama (&lt;a href=&#34;https://github.com/taketwo/llm-ollama&#34;&gt;llm-ollama&lt;/a&gt;) and Hugging Face models via &lt;a href=&#34;https://github.com/ml-explore/mlx&#34;&gt;MLX&lt;/a&gt; (&lt;a href=&#34;https://github.com/simonw/llm-mlx&#34;&gt;llm-mlx&lt;/a&gt;). I was able to use both approaches to test out some local models.&lt;/p&gt;&#xA;&lt;h2 id=&#34;which-models&#34;&gt;Which Models&lt;/h2&gt;&#xA;&lt;p&gt;There are a mind-boggling number of local models and variations to choose from. Hugging Face has a &lt;a href=&#34;https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard#/&#34;&gt;leader board&lt;/a&gt;. Based on what I saw other people talking about, I decided to start with a few:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Ollama mistral-small3.1:24B (4bit) - 15GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Ollama gemma3:27B (4bit) - 17GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Qwen2.5-0.5B-Instruct-4bit — 278 MB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Mistral-7B-Instruct-v0.3-4bit — 4.08 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Mistral-Small-24B-Instruct-2501-4bit — 13.26 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Llama-3.3-70B-Instruct-4bit — 40 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;You&amp;rsquo;ll notice a few specifications, like &lt;strong&gt;24B&lt;/strong&gt; and &lt;strong&gt;4bit&lt;/strong&gt;. These relate to some performance tradeoffs you need to consider when running LLMs on your laptop.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Model size&lt;/strong&gt; is specified by the number of parameters the model supports, 7B is 7 billion and 27B is 27 billion. Generally, the more parameters a model supports, the better the model is at reasoning and following instructions.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Quantization&lt;/strong&gt; (4-bit, 8-bit, 16-bit) is about how precisely the model stores its weights. Lower quantization means smaller files sizes and less RAM is required to run the model. The downside is less accuracy or ability to reason.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;Once I had a few local models installed and ready to go, it was time to try them out with &lt;code&gt;ToolAgent&lt;/code&gt;! Because &lt;code&gt;ToolAgent&lt;/code&gt; is based on the &lt;code&gt;llm&lt;/code&gt; library, I just had to pass the model name using the rules for the plugin (&lt;code&gt;llm-ollama&lt;/code&gt; or &lt;code&gt;llm-mlx&lt;/code&gt;) I wanted to use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; Local models are slower than cloud-based models. Ollama and MLX use GPU on my Macbook Pro M1 with 64GB, but it&amp;rsquo;s sluggish. MLX is built for Apple silicon, so I expect the model was working as well as it could on a single machine.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Reasoning:&lt;/strong&gt; The smaller models (&lt;code&gt;Qwen2.5-0.5B&lt;/code&gt; and &lt;code&gt;Mistral-7B&lt;/code&gt;) were not able to function as an agent. They couldn&amp;rsquo;t follow the instructions in the system prompt and they couldn&amp;rsquo;t call any tool functions. The larger models (&lt;code&gt;Mistral-Small-24B&lt;/code&gt; and &lt;code&gt;Llama-3.3-70B&lt;/code&gt;) were able to function as agents. They didn&amp;rsquo;t work as smoothly as the cloud-based models, but they were very functional.&lt;/p&gt;&#xA;&lt;p&gt;An exchange with &lt;code&gt;Mistral-Small-24B&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: what is tomorrow&amp;#39;s date?&#xA;&#xA;Thought: &amp;#34;I need to determine the current date and time to calculate tomorrow&amp;#39;s date.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_datetime&amp;#34;, &amp;#34;param&amp;#34;: null&#xA;&#xA;Thought: &amp;#34;I have the current date, which is 2025-04-13. To find tomorrow&amp;#39;s date, I need to add one day to the current date.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;calculate&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;2025-04-13 + 1 day&amp;#34;&#xA;&#xA;Thought: &amp;#34;I cannot calculate dates directly. I need to determine the date by using the current date and adding one day manually.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output text ...&amp;#34;&#xA;&#xA;Agent: Tomorrow&amp;#39;s date is April 14, 2025.&#xA;&#xA;You: what dentist appointments are available tomorrow?&#xA;Thought: &amp;#34;I need to determine the available dentist appointments for tomorrow. First, I need to check the available specialties to ensure &amp;#39;dentist&amp;#39; is one of them.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_appointment_specialties&amp;#34;, &amp;#34;param&amp;#34;: null&#xA;&#xA;Thought: &amp;#34;I have confirmed that &amp;#39;dentist&amp;#39; is an available specialty. Next, I need to find the available dentist appointments for tomorrow.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_available_appointments&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;dentist&amp;#34;&#xA;&#xA;Thought: &amp;#34;I have the list of available dentist appointments. I need to filter the appointments for tomorrow&amp;#39;s date, which is April 14, 2025.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output text ...&amp;#34;&#xA;&#xA;Agent: Here are the available dentist appointments for tomorrow, April 14, 2025:&#xA;Appointment ID: 3, Time: 11:00 AM&#xA;Appointment ID: 4, Time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It made a bad assumption with calculating the next day, but recovered. Otherwise worked well.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I have started to look at real agent frameworks, like &lt;a href=&#34;https://ai.pydantic.dev/&#34;&gt;Pydantic-AI&lt;/a&gt;. I think I have learned enough of the basics to be dangerous and it&amp;rsquo;s time to move beyond my own toy framework. Google released an &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; at Google Next. It supports many of the features I have been playing with, including tool calling, planning, MCP, and local models. It also supports some advanced concepts like agent orchestration.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s start exploring Google&amp;rsquo;s ADK!&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I finally got around to exploring local models, which is surprisingly simple to set up. I wanted to see how well a local model would perform in &lt;code&gt;ToolAgent&lt;/code&gt; compared to the remote models (mostly Gemini) I have been using.&lt;/p&gt;&#xA;&lt;p&gt;For more context on my explorations , checkout the previous posts: &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/&#34;&gt;WebAgent Gets a Refactor&lt;/a&gt;, and &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-model-context-protocol/&#34;&gt;Model Context Protocol (MCP)&lt;/a&gt;. Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;local-models&#34;&gt;Local Models&lt;/h2&gt;&#xA;&lt;p&gt;Local large language models (LLMs) are models that can be run directly on a personal device, such as a laptop, desktop computer, or smartphone, rather than relying on cloud-based servers. They offer features like privacy, security, compliance, offline availability, and lower operating costs.&lt;/p&gt;&#xA;&lt;p&gt;One of the easiest ways to get started with local models is with &lt;a href=&#34;https://ollama.com/&#34;&gt;Ollama&lt;/a&gt;, a system to download, manage, and run models on your laptop. Another frequently used resource is &lt;a href=&#34;https://huggingface.co/&#34;&gt;Hugging Face&lt;/a&gt;, which I&amp;rsquo;ve heard called the &amp;ldquo;GitHub of Models&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;My &lt;code&gt;ToolAgent&lt;/code&gt; is based on &lt;a href=&#34;https://simonwillison.net/&#34;&gt;Simon Willison&amp;rsquo;s&lt;/a&gt; &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;&lt;code&gt;llm&lt;/code&gt; project,&lt;/a&gt; which has &lt;a href=&#34;https://llm.datasette.io/en/stable/plugins/directory.html#local-models&#34;&gt;plugins&lt;/a&gt; for several different local model system, including Ollama (&lt;a href=&#34;https://github.com/taketwo/llm-ollama&#34;&gt;llm-ollama&lt;/a&gt;) and Hugging Face models via &lt;a href=&#34;https://github.com/ml-explore/mlx&#34;&gt;MLX&lt;/a&gt; (&lt;a href=&#34;https://github.com/simonw/llm-mlx&#34;&gt;llm-mlx&lt;/a&gt;). I was able to use both approaches to test out some local models.&lt;/p&gt;&#xA;&lt;h2 id=&#34;which-models&#34;&gt;Which Models&lt;/h2&gt;&#xA;&lt;p&gt;There are a mind-boggling number of local models and variations to choose from. Hugging Face has a &lt;a href=&#34;https://huggingface.co/spaces/open-llm-leaderboard/open_llm_leaderboard#/&#34;&gt;leader board&lt;/a&gt;. Based on what I saw other people talking about, I decided to start with a few:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;Ollama mistral-small3.1:24B (4bit) - 15GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;Ollama gemma3:27B (4bit) - 17GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Qwen2.5-0.5B-Instruct-4bit — 278 MB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Mistral-7B-Instruct-v0.3-4bit — 4.08 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Mistral-Small-24B-Instruct-2501-4bit — 13.26 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;mlx-community/Llama-3.3-70B-Instruct-4bit — 40 GB&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;You&amp;rsquo;ll notice a few specifications, like &lt;strong&gt;24B&lt;/strong&gt; and &lt;strong&gt;4bit&lt;/strong&gt;. These relate to some performance tradeoffs you need to consider when running LLMs on your laptop.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Model size&lt;/strong&gt; is specified by the number of parameters the model supports, 7B is 7 billion and 27B is 27 billion. Generally, the more parameters a model supports, the better the model is at reasoning and following instructions.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Quantization&lt;/strong&gt; (4-bit, 8-bit, 16-bit) is about how precisely the model stores its weights. Lower quantization means smaller files sizes and less RAM is required to run the model. The downside is less accuracy or ability to reason.&lt;/p&gt;&#xA;&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;&#xA;&lt;p&gt;Once I had a few local models installed and ready to go, it was time to try them out with &lt;code&gt;ToolAgent&lt;/code&gt;! Because &lt;code&gt;ToolAgent&lt;/code&gt; is based on the &lt;code&gt;llm&lt;/code&gt; library, I just had to pass the model name using the rules for the plugin (&lt;code&gt;llm-ollama&lt;/code&gt; or &lt;code&gt;llm-mlx&lt;/code&gt;) I wanted to use.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Speed:&lt;/strong&gt; Local models are slower than cloud-based models. Ollama and MLX use GPU on my Macbook Pro M1 with 64GB, but it&amp;rsquo;s sluggish. MLX is built for Apple silicon, so I expect the model was working as well as it could on a single machine.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Reasoning:&lt;/strong&gt; The smaller models (&lt;code&gt;Qwen2.5-0.5B&lt;/code&gt; and &lt;code&gt;Mistral-7B&lt;/code&gt;) were not able to function as an agent. They couldn&amp;rsquo;t follow the instructions in the system prompt and they couldn&amp;rsquo;t call any tool functions. The larger models (&lt;code&gt;Mistral-Small-24B&lt;/code&gt; and &lt;code&gt;Llama-3.3-70B&lt;/code&gt;) were able to function as agents. They didn&amp;rsquo;t work as smoothly as the cloud-based models, but they were very functional.&lt;/p&gt;&#xA;&lt;p&gt;An exchange with &lt;code&gt;Mistral-Small-24B&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: what is tomorrow&amp;#39;s date?&#xA;&#xA;Thought: &amp;#34;I need to determine the current date and time to calculate tomorrow&amp;#39;s date.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_datetime&amp;#34;, &amp;#34;param&amp;#34;: null&#xA;&#xA;Thought: &amp;#34;I have the current date, which is 2025-04-13. To find tomorrow&amp;#39;s date, I need to add one day to the current date.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;calculate&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;2025-04-13 + 1 day&amp;#34;&#xA;&#xA;Thought: &amp;#34;I cannot calculate dates directly. I need to determine the date by using the current date and adding one day manually.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output text ...&amp;#34;&#xA;&#xA;Agent: Tomorrow&amp;#39;s date is April 14, 2025.&#xA;&#xA;You: what dentist appointments are available tomorrow?&#xA;Thought: &amp;#34;I need to determine the available dentist appointments for tomorrow. First, I need to check the available specialties to ensure &amp;#39;dentist&amp;#39; is one of them.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_appointment_specialties&amp;#34;, &amp;#34;param&amp;#34;: null&#xA;&#xA;Thought: &amp;#34;I have confirmed that &amp;#39;dentist&amp;#39; is an available specialty. Next, I need to find the available dentist appointments for tomorrow.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_available_appointments&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;dentist&amp;#34;&#xA;&#xA;Thought: &amp;#34;I have the list of available dentist appointments. I need to filter the appointments for tomorrow&amp;#39;s date, which is April 14, 2025.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output text ...&amp;#34;&#xA;&#xA;Agent: Here are the available dentist appointments for tomorrow, April 14, 2025:&#xA;Appointment ID: 3, Time: 11:00 AM&#xA;Appointment ID: 4, Time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It made a bad assumption with calculating the next day, but recovered. Otherwise worked well.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I have started to look at real agent frameworks, like &lt;a href=&#34;https://ai.pydantic.dev/&#34;&gt;Pydantic-AI&lt;/a&gt;. I think I have learned enough of the basics to be dangerous and it&amp;rsquo;s time to move beyond my own toy framework. Google released an &lt;a href=&#34;https://google.github.io/adk-docs/&#34;&gt;Agent Development Kit&lt;/a&gt; at Google Next. It supports many of the features I have been playing with, including tool calling, planning, MCP, and local models. It also supports some advanced concepts like agent orchestration.&lt;/p&gt;&#xA;&lt;p&gt;Let&amp;rsquo;s start exploring Google&amp;rsquo;s ADK!&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Model Context Protocol</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-model-context-protocol/</link>
				<pubDate>Sun, 30 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-model-context-protocol/</guid>
				<description>&lt;p&gt;I will likely run out of topics to cover in the &amp;ldquo;LLMs as Agents&amp;rdquo; series, eventually. I wanted to explore creating a chat UI and what I could do with embeddings but I didn&amp;rsquo;t do that. For more context , checkout the previous posts: &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;, and &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/&#34;&gt;WebAgent Gets a Refactor&lt;/a&gt;. Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m back to cover the new hotness in the agent space: &lt;a href=&#34;https://modelcontextprotocol.io/introduction&#34;&gt;Model Context Protocol&lt;/a&gt; (MCP) from &lt;a href=&#34;https://www.anthropic.com/news/model-context-protocol&#34;&gt;Anthropic&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;MCP is having its jQuery moment, and that&amp;rsquo;s not an insult. Even &lt;a href=&#34;https://openai.github.io/openai-agents-python/mcp/&#34;&gt;OpenAI is adopting MCP&lt;/a&gt;. I&amp;rsquo;ve learned about hooking tools up to LLM-based agents, and it was always clear that competing standards for &lt;strong&gt;&lt;em&gt;how&lt;/em&gt;&lt;/strong&gt; to connect LLM-based agents to tools would create fragmentation and hurt growth. MCP is an &lt;strong&gt;open&lt;/strong&gt; protocol for building agents. The MCP system contains:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Hosts&lt;/strong&gt;: Programs like Claude Desktop, IDEs, or other tools that contain a client&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Clients&lt;/strong&gt;: Protocol clients that maintain 1:1 connections with servers&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Servers&lt;/strong&gt;: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;There is a ton of information available for you to learn about MCP. This post is not teaching you about MCP.&lt;/p&gt;&#xA;&lt;p&gt;This post is about MCP-enabling the &lt;code&gt;ToolProvider&lt;/code&gt; and &lt;code&gt;ToolAgent&lt;/code&gt; framework I created! That&amp;rsquo;s right, I&amp;rsquo;m exposing &lt;code&gt;ToolProviders&lt;/code&gt; as MCP Servers and &lt;code&gt;ToolAgent&lt;/code&gt; as an MCP Client — and I&amp;rsquo;m doing it with &lt;strong&gt;zero dependencies&lt;/strong&gt; on any MCP framework. I like to see how things work under the covers. Having spent time building Browsers, I also believe that any open protocol needs to have multiple implementations, even crappy ones like mine.&lt;/p&gt;&#xA;&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;&#xA;&lt;p&gt;Before I started building an MCP Client and Server from scratch, I made a few decisions:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was going to use Copilot to help do the work. It just seemed silly to try this otherwise.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I copy/pasted many sections of the &lt;a href=&#34;https://spec.modelcontextprotocol.io/specification/2025-03-26/&#34;&gt;MCP Specification&lt;/a&gt; into a single, long Markdown document. I had to give Copilot the right context. I also used the &lt;a href=&#34;https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-03-26/schema.json&#34;&gt;MCP Schema&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was going to implement the &lt;a href=&#34;https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#stdio&#34;&gt;stdio transport&lt;/a&gt;. HTTP SSE and WebSockets (I think) are also available, but a local system was good enough to learn how this works.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was only going to support &amp;ldquo;Tools&amp;rdquo; in my Client and Server for now. You can also support &amp;ldquo;Resources&amp;rdquo; and &amp;ldquo;Prompts&amp;rdquo;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;toolprovider-mcp-server&#34;&gt;ToolProvider MCP Server&lt;/h2&gt;&#xA;&lt;p&gt;Copilot handled all the heavy lifting here. I asked to create an MCP Server, using the MCP specification, that was implemented using &lt;code&gt;ToolProviders&lt;/code&gt;. &lt;code&gt;ToolProviders&lt;/code&gt; already supported everything an MCP Server needed to support tools, so Copilot just had to map from my basic JSON schema to the JSON schema used by MCP.&lt;/p&gt;&#xA;&lt;p&gt;Copilot was able to create a basic stdio-based request/response system, and was able to add the JSON-RPC message passing. Copilot was able to glean some of the messages and the message flows from the MCP specification.&lt;/p&gt;&#xA;&lt;p&gt;I install Claude Desktop to test it out. It didn&amp;rsquo;t work on the first try. We missed some nuance of the JSON-RPC message passing, and we had to update some message types based on the newer schema. It did not take long before we had my &lt;code&gt;ToolProvider&lt;/code&gt; tools running in Claude as an MCP Server.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/claude-tool-provider-998x1024.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;toolagent-mcp-client&#34;&gt;ToolAgent MCP Client&lt;/h2&gt;&#xA;&lt;p&gt;Again, Copilot did most of the work. We already had a basic stdio-based system working, and we had several other examples of &lt;code&gt;ToolAgent&lt;/code&gt; being used in different ways: terminal chat, web-based chat, and some tests — so it didn&amp;rsquo;t take long to have a a basic system with &lt;code&gt;MCPToolProvider&lt;/code&gt; (since &lt;code&gt;ToolAgent&lt;/code&gt; connects to &lt;code&gt;ToolProviders&lt;/code&gt;, we have to map MCP Servers to &lt;code&gt;ToolProviders&lt;/code&gt;) and an &lt;code&gt;ToolAgentMCPClient&lt;/code&gt; that wraps &lt;code&gt;ToolAgent&lt;/code&gt; itself.&lt;/p&gt;&#xA;&lt;p&gt;I also installed a 3rd party MCP Server (&lt;a href=&#34;https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem&#34;&gt;&lt;code&gt;filesystem&lt;/code&gt;&lt;/a&gt;) so I could test a Server that wasn&amp;rsquo;t something I wrote.&lt;/p&gt;&#xA;&lt;p&gt;We had to tweak the Client stdio request/response mechanism to get things working reliably. Timing, buffering, and flushing are important! Because the Client is spawning Servers as processes, I had to add some code to make sure the Client was cleaning up the spawned Servers on shutdown or in failure situations.&lt;/p&gt;&#xA;&lt;p&gt;I was able to get both &lt;code&gt;ToolProvider&lt;/code&gt; MCP Server and &lt;code&gt;filesystem&lt;/code&gt; MCP Server working in my &lt;code&gt;ToolAgent&lt;/code&gt;-based MCP Client.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Connecting to MCP server: filesystem&#xA;Connected to secure-filesystem-server v0.2.0&#xA;Available tools: 11&#xA;Connecting to MCP server: mock&#xA;Connected to ToolProvider MCP Server v0.1.0&#xA;Available tools: 19&#xA;&#xA;Chat with the agent (type &amp;#39;exit&amp;#39; to quit, &amp;#39;tools&amp;#39; to list tools):&#xA;&amp;gt; What files do I have in my Desktop folder?&#xA;&#xA;Thought: &amp;#34;I need to find out which directories I have access to before I can list the files in the Desktop folder.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_allowed_directories&amp;#34;&#xA;&#xA;Thought: &amp;#34;I can now list the files in the Desktop folder since I know I have access to it.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_directory&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;/Users/mfinkle-mac/Desktop&amp;#34;&#xA;&#xA;Thought: &amp;#34;I made a mistake in the previous JSON format. The parameter for list_directory should be an object.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_directory&amp;#34;, &amp;#34;param&amp;#34;: { &amp;#34;path&amp;#34;: &amp;#34;/Users/mfinkle-mac/Desktop&amp;#34;}&#xA;&#xA;Thought: &amp;#34;I have successfully listed the files and directories in the Desktop folder.&amp;#34;,&#xA;&#xA;Agent: Here are the files and directories in your Desktop folder:&#xA;.DS_Store&#xA;.localized&#xA;Reactions (directory)&#xA;Screen Recording 2024-06-24 at 5.12.08 PM.mov&#xA;Screen Recording 2024-07-19 at 10.30.18 AM.mov&#xA;Screen Recording 2024-11-04 at 12.53.58 PM.mov&#xA;Screen Recording 2025-03-17 at 11.56.51 AM.mov&#xA;Stuff (directory)&#xA;live-chat-sample.mov&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see &lt;code&gt;ToolAgent&lt;/code&gt; processing the requests, but it&amp;rsquo;s using the &lt;code&gt;filesystem&lt;/code&gt; MCP Server to perform the actions.&lt;/p&gt;&#xA;&lt;p&gt;The code for both the &lt;code&gt;ToolProvider&lt;/code&gt; MCP Server and the &lt;code&gt;ToolAgent&lt;/code&gt; MCP Client are in the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;LLMs Agents repo&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;p&gt;MCP is a very handy mechanism to connect LLM-based agents to different types of tools and resources. We&amp;rsquo;ve seen &amp;ldquo;broker&amp;rdquo; or &amp;ldquo;middleware&amp;rdquo; systems like this in the industry before and there is usually a lot of benefit for the ecosystem while a dominate player exists. I remember using COM and ODBC.&lt;/p&gt;&#xA;&lt;p&gt;There are a lot of MCP Servers being created everyday (&lt;a href=&#34;https://github.com/punkpeye/awesome-mcp-servers&#34;&gt;curated list&lt;/a&gt;). Many are of questionable value, but that&amp;rsquo;s the beauty to the early adopter phase. We&amp;rsquo;ll start to see some examples that push the boundaries, and that will be interesting.&lt;/p&gt;&#xA;&lt;p&gt;There are fewer MCP Clients, but that will certainly change. I wonder how people (or IT/Security) will feel about all of this data being moved around. It makes me want to start looking into local models (another topic I failed to start).&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I want to refactor the &lt;code&gt;ToolProvider&lt;/code&gt; approach to defining tools. The MCP approach is cleaner and would make the mapping code (from &lt;code&gt;ToolProvider&lt;/code&gt; to MCP Server) simpler too. The Playwright team released a &lt;a href=&#34;https://github.com/microsoft/playwright-mcp&#34;&gt;Playwright MCP Server&lt;/a&gt;, which when used in my &lt;code&gt;ToolAgent&lt;/code&gt; MCP Client could mean &lt;code&gt;WebAgent&lt;/code&gt; doesn&amp;rsquo;t need to exist anymore.&lt;/p&gt;&#xA;&lt;p&gt;With the growing number of easy to connect MCP Servers, I want to start investigating ways to orchestrate all those tools into a meaningful and useful Agent.&lt;/p&gt;&#xA;&lt;p&gt;Also:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Local Models&lt;/li&gt;&#xA;&lt;li&gt;Chat UI (and non-Chat UI) experiences&lt;/li&gt;&#xA;&lt;li&gt;Embedding and Vector search&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I will likely run out of topics to cover in the &amp;ldquo;LLMs as Agents&amp;rdquo; series, eventually. I wanted to explore creating a chat UI and what I could do with embeddings but I didn&amp;rsquo;t do that. For more context , checkout the previous posts: &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;, &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;, and &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/&#34;&gt;WebAgent Gets a Refactor&lt;/a&gt;. Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m back to cover the new hotness in the agent space: &lt;a href=&#34;https://modelcontextprotocol.io/introduction&#34;&gt;Model Context Protocol&lt;/a&gt; (MCP) from &lt;a href=&#34;https://www.anthropic.com/news/model-context-protocol&#34;&gt;Anthropic&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;MCP is having its jQuery moment, and that&amp;rsquo;s not an insult. Even &lt;a href=&#34;https://openai.github.io/openai-agents-python/mcp/&#34;&gt;OpenAI is adopting MCP&lt;/a&gt;. I&amp;rsquo;ve learned about hooking tools up to LLM-based agents, and it was always clear that competing standards for &lt;strong&gt;&lt;em&gt;how&lt;/em&gt;&lt;/strong&gt; to connect LLM-based agents to tools would create fragmentation and hurt growth. MCP is an &lt;strong&gt;open&lt;/strong&gt; protocol for building agents. The MCP system contains:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Hosts&lt;/strong&gt;: Programs like Claude Desktop, IDEs, or other tools that contain a client&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Clients&lt;/strong&gt;: Protocol clients that maintain 1:1 connections with servers&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;MCP Servers&lt;/strong&gt;: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;There is a ton of information available for you to learn about MCP. This post is not teaching you about MCP.&lt;/p&gt;&#xA;&lt;p&gt;This post is about MCP-enabling the &lt;code&gt;ToolProvider&lt;/code&gt; and &lt;code&gt;ToolAgent&lt;/code&gt; framework I created! That&amp;rsquo;s right, I&amp;rsquo;m exposing &lt;code&gt;ToolProviders&lt;/code&gt; as MCP Servers and &lt;code&gt;ToolAgent&lt;/code&gt; as an MCP Client — and I&amp;rsquo;m doing it with &lt;strong&gt;zero dependencies&lt;/strong&gt; on any MCP framework. I like to see how things work under the covers. Having spent time building Browsers, I also believe that any open protocol needs to have multiple implementations, even crappy ones like mine.&lt;/p&gt;&#xA;&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;&#xA;&lt;p&gt;Before I started building an MCP Client and Server from scratch, I made a few decisions:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was going to use Copilot to help do the work. It just seemed silly to try this otherwise.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I copy/pasted many sections of the &lt;a href=&#34;https://spec.modelcontextprotocol.io/specification/2025-03-26/&#34;&gt;MCP Specification&lt;/a&gt; into a single, long Markdown document. I had to give Copilot the right context. I also used the &lt;a href=&#34;https://github.com/modelcontextprotocol/specification/blob/main/schema/2025-03-26/schema.json&#34;&gt;MCP Schema&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was going to implement the &lt;a href=&#34;https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#stdio&#34;&gt;stdio transport&lt;/a&gt;. HTTP SSE and WebSockets (I think) are also available, but a local system was good enough to learn how this works.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I was only going to support &amp;ldquo;Tools&amp;rdquo; in my Client and Server for now. You can also support &amp;ldquo;Resources&amp;rdquo; and &amp;ldquo;Prompts&amp;rdquo;.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;toolprovider-mcp-server&#34;&gt;ToolProvider MCP Server&lt;/h2&gt;&#xA;&lt;p&gt;Copilot handled all the heavy lifting here. I asked to create an MCP Server, using the MCP specification, that was implemented using &lt;code&gt;ToolProviders&lt;/code&gt;. &lt;code&gt;ToolProviders&lt;/code&gt; already supported everything an MCP Server needed to support tools, so Copilot just had to map from my basic JSON schema to the JSON schema used by MCP.&lt;/p&gt;&#xA;&lt;p&gt;Copilot was able to create a basic stdio-based request/response system, and was able to add the JSON-RPC message passing. Copilot was able to glean some of the messages and the message flows from the MCP specification.&lt;/p&gt;&#xA;&lt;p&gt;I install Claude Desktop to test it out. It didn&amp;rsquo;t work on the first try. We missed some nuance of the JSON-RPC message passing, and we had to update some message types based on the newer schema. It did not take long before we had my &lt;code&gt;ToolProvider&lt;/code&gt; tools running in Claude as an MCP Server.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/claude-tool-provider-998x1024.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;toolagent-mcp-client&#34;&gt;ToolAgent MCP Client&lt;/h2&gt;&#xA;&lt;p&gt;Again, Copilot did most of the work. We already had a basic stdio-based system working, and we had several other examples of &lt;code&gt;ToolAgent&lt;/code&gt; being used in different ways: terminal chat, web-based chat, and some tests — so it didn&amp;rsquo;t take long to have a a basic system with &lt;code&gt;MCPToolProvider&lt;/code&gt; (since &lt;code&gt;ToolAgent&lt;/code&gt; connects to &lt;code&gt;ToolProviders&lt;/code&gt;, we have to map MCP Servers to &lt;code&gt;ToolProviders&lt;/code&gt;) and an &lt;code&gt;ToolAgentMCPClient&lt;/code&gt; that wraps &lt;code&gt;ToolAgent&lt;/code&gt; itself.&lt;/p&gt;&#xA;&lt;p&gt;I also installed a 3rd party MCP Server (&lt;a href=&#34;https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem&#34;&gt;&lt;code&gt;filesystem&lt;/code&gt;&lt;/a&gt;) so I could test a Server that wasn&amp;rsquo;t something I wrote.&lt;/p&gt;&#xA;&lt;p&gt;We had to tweak the Client stdio request/response mechanism to get things working reliably. Timing, buffering, and flushing are important! Because the Client is spawning Servers as processes, I had to add some code to make sure the Client was cleaning up the spawned Servers on shutdown or in failure situations.&lt;/p&gt;&#xA;&lt;p&gt;I was able to get both &lt;code&gt;ToolProvider&lt;/code&gt; MCP Server and &lt;code&gt;filesystem&lt;/code&gt; MCP Server working in my &lt;code&gt;ToolAgent&lt;/code&gt;-based MCP Client.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Connecting to MCP server: filesystem&#xA;Connected to secure-filesystem-server v0.2.0&#xA;Available tools: 11&#xA;Connecting to MCP server: mock&#xA;Connected to ToolProvider MCP Server v0.1.0&#xA;Available tools: 19&#xA;&#xA;Chat with the agent (type &amp;#39;exit&amp;#39; to quit, &amp;#39;tools&amp;#39; to list tools):&#xA;&amp;gt; What files do I have in my Desktop folder?&#xA;&#xA;Thought: &amp;#34;I need to find out which directories I have access to before I can list the files in the Desktop folder.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_allowed_directories&amp;#34;&#xA;&#xA;Thought: &amp;#34;I can now list the files in the Desktop folder since I know I have access to it.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_directory&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;/Users/mfinkle-mac/Desktop&amp;#34;&#xA;&#xA;Thought: &amp;#34;I made a mistake in the previous JSON format. The parameter for list_directory should be an object.&amp;#34;,&#xA;&amp;#34;type&amp;#34;: &amp;#34;call_tool&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;list_directory&amp;#34;, &amp;#34;param&amp;#34;: { &amp;#34;path&amp;#34;: &amp;#34;/Users/mfinkle-mac/Desktop&amp;#34;}&#xA;&#xA;Thought: &amp;#34;I have successfully listed the files and directories in the Desktop folder.&amp;#34;,&#xA;&#xA;Agent: Here are the files and directories in your Desktop folder:&#xA;.DS_Store&#xA;.localized&#xA;Reactions (directory)&#xA;Screen Recording 2024-06-24 at 5.12.08 PM.mov&#xA;Screen Recording 2024-07-19 at 10.30.18 AM.mov&#xA;Screen Recording 2024-11-04 at 12.53.58 PM.mov&#xA;Screen Recording 2025-03-17 at 11.56.51 AM.mov&#xA;Stuff (directory)&#xA;live-chat-sample.mov&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You can see &lt;code&gt;ToolAgent&lt;/code&gt; processing the requests, but it&amp;rsquo;s using the &lt;code&gt;filesystem&lt;/code&gt; MCP Server to perform the actions.&lt;/p&gt;&#xA;&lt;p&gt;The code for both the &lt;code&gt;ToolProvider&lt;/code&gt; MCP Server and the &lt;code&gt;ToolAgent&lt;/code&gt; MCP Client are in the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;LLMs Agents repo&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;p&gt;MCP is a very handy mechanism to connect LLM-based agents to different types of tools and resources. We&amp;rsquo;ve seen &amp;ldquo;broker&amp;rdquo; or &amp;ldquo;middleware&amp;rdquo; systems like this in the industry before and there is usually a lot of benefit for the ecosystem while a dominate player exists. I remember using COM and ODBC.&lt;/p&gt;&#xA;&lt;p&gt;There are a lot of MCP Servers being created everyday (&lt;a href=&#34;https://github.com/punkpeye/awesome-mcp-servers&#34;&gt;curated list&lt;/a&gt;). Many are of questionable value, but that&amp;rsquo;s the beauty to the early adopter phase. We&amp;rsquo;ll start to see some examples that push the boundaries, and that will be interesting.&lt;/p&gt;&#xA;&lt;p&gt;There are fewer MCP Clients, but that will certainly change. I wonder how people (or IT/Security) will feel about all of this data being moved around. It makes me want to start looking into local models (another topic I failed to start).&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;I want to refactor the &lt;code&gt;ToolProvider&lt;/code&gt; approach to defining tools. The MCP approach is cleaner and would make the mapping code (from &lt;code&gt;ToolProvider&lt;/code&gt; to MCP Server) simpler too. The Playwright team released a &lt;a href=&#34;https://github.com/microsoft/playwright-mcp&#34;&gt;Playwright MCP Server&lt;/a&gt;, which when used in my &lt;code&gt;ToolAgent&lt;/code&gt; MCP Client could mean &lt;code&gt;WebAgent&lt;/code&gt; doesn&amp;rsquo;t need to exist anymore.&lt;/p&gt;&#xA;&lt;p&gt;With the growing number of easy to connect MCP Servers, I want to start investigating ways to orchestrate all those tools into a meaningful and useful Agent.&lt;/p&gt;&#xA;&lt;p&gt;Also:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Local Models&lt;/li&gt;&#xA;&lt;li&gt;Chat UI (and non-Chat UI) experiences&lt;/li&gt;&#xA;&lt;li&gt;Embedding and Vector search&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Search</title>
				<link>https://starkravingfinkle.org/search/</link>
				<pubDate>Mon, 24 Mar 2025 23:00:00 -0300</pubDate>
				<guid>https://starkravingfinkle.org/search/</guid>
				<description>&lt;h1 id=&#34;search&#34;&gt;Search&lt;/h1&gt;&#xA;&lt;p&gt;Here you can search for indexable pages, like posts and alike. There&amp;rsquo;s also support for URL param &amp;ldquo;q&amp;rdquo; which then auto searches upon page load event.&lt;/p&gt;&#xA;&lt;p class=&#34;hidden&#34;&gt;It&amp;#39;s necessary to enable Javascript&lt;/p&gt;&#xA;&lt;p class=&#34;search-loading hidden&#34;&gt;Loading...&lt;/p&gt;&#xA;&#xA;&lt;form id=&#34;search-form&#34; class=&#34;search-form&#34; action=&#34;#&#34; method=&#34;post&#34; accept-charset=&#34;UTF-8&#34; role=&#34;search&#34;&gt;&#xA;  &lt;div class=&#34;search-bar&#34;&gt;&#xA;    &lt;label for=&#34;query&#34; class=&#34;hidden&#34;&gt;&lt;/label&gt;&#xA;    &lt;input id=&#34;query&#34; class=&#34;search-text&#34; type=&#34;text&#34; placeholder=&#34;Search...&#34;/&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/form&gt;&#xA;&#xA;&lt;div class=&#34;search-results&#34;&gt;&lt;/div&gt;&#xA;&#xA;&lt;template&gt;&#xA;  &lt;article class=&#34;search-result list-view&#34;&gt;&#xA;    &lt;header&gt;&#xA;      &lt;h2 class=&#34;title&#34;&gt;&lt;a href=&#34;#&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&#xA;      &lt;div class=&#34;submitted&#34;&gt;&#xA;        &lt;time class=&#34;created-date&#34;&gt;&lt;/time&gt;&#xA;      &lt;/div&gt;&#xA;    &lt;/header&gt;&#xA;    &lt;p class=&#34;content&#34;&gt;&lt;/p&gt;&#xA;  &lt;/article&gt;&#xA;&lt;/template&gt;&#xA;&#xA;</description>
				<content:encoded>&lt;h1 id=&#34;search&#34;&gt;Search&lt;/h1&gt;&#xA;&lt;p&gt;Here you can search for indexable pages, like posts and alike. There&amp;rsquo;s also support for URL param &amp;ldquo;q&amp;rdquo; which then auto searches upon page load event.&lt;/p&gt;&#xA;&lt;p class=&#34;hidden&#34;&gt;It&amp;#39;s necessary to enable Javascript&lt;/p&gt;&#xA;&lt;p class=&#34;search-loading hidden&#34;&gt;Loading...&lt;/p&gt;&#xA;&#xA;&lt;form id=&#34;search-form&#34; class=&#34;search-form&#34; action=&#34;#&#34; method=&#34;post&#34; accept-charset=&#34;UTF-8&#34; role=&#34;search&#34;&gt;&#xA;  &lt;div class=&#34;search-bar&#34;&gt;&#xA;    &lt;label for=&#34;query&#34; class=&#34;hidden&#34;&gt;&lt;/label&gt;&#xA;    &lt;input id=&#34;query&#34; class=&#34;search-text&#34; type=&#34;text&#34; placeholder=&#34;Search...&#34;/&gt;&#xA;  &lt;/div&gt;&#xA;&lt;/form&gt;&#xA;&#xA;&lt;div class=&#34;search-results&#34;&gt;&lt;/div&gt;&#xA;&#xA;&lt;template&gt;&#xA;  &lt;article class=&#34;search-result list-view&#34;&gt;&#xA;    &lt;header&gt;&#xA;      &lt;h2 class=&#34;title&#34;&gt;&lt;a href=&#34;#&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&#xA;      &lt;div class=&#34;submitted&#34;&gt;&#xA;        &lt;time class=&#34;created-date&#34;&gt;&lt;/time&gt;&#xA;      &lt;/div&gt;&#xA;    &lt;/header&gt;&#xA;    &lt;p class=&#34;content&#34;&gt;&lt;/p&gt;&#xA;  &lt;/article&gt;&#xA;&lt;/template&gt;&#xA;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: WebAgent Gets a Refactor</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/</link>
				<pubDate>Thu, 20 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-webagent-gets-a-refactor/</guid>
				<description>&lt;p&gt;My fifth post on LLMs as Agents. I just it&amp;rsquo;s a series now.&lt;/p&gt;&#xA;&lt;p&gt;After refactoring &lt;code&gt;ToolAgent&lt;/code&gt;, I decided to do the same with &lt;code&gt;WebAgent&lt;/code&gt;, building it on the &lt;code&gt;ToolAgent&lt;/code&gt; system. For more context and a refresher on &lt;code&gt;WebAgent&lt;/code&gt;, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;: I looked at ways to improve the agent outcomes by using better approaches to planning. I settled on using ReAct, with some Few-Shot prompts, and had the LLM do some Chain of Thought (CoT) output.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;: I refactored the &lt;code&gt;ToolAgent&lt;/code&gt; code into a reusable base class, added some additional mock tools, and even some basic benchmarking.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;webagent&#34;&gt;WebAgent&lt;/h2&gt;&#xA;&lt;p&gt;After refactoring &lt;code&gt;ToolAgent&lt;/code&gt;, I decided to see if I could build &lt;code&gt;WebAgent&lt;/code&gt; on top of &lt;code&gt;ToolAgent&lt;/code&gt;. I think it turned out pretty nicely. Again, Copilot helped too.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;WebAgent&lt;/code&gt; now derives from &lt;code&gt;ToolAgent&lt;/code&gt;, and &lt;code&gt;PageManager&lt;/code&gt; (the wrapper around Playwright for browser automation) became a &lt;code&gt;ToolProvider&lt;/code&gt;. All of the wrappers for Playwright automation became tools the agent could use. This worked better than I thought.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Starting task: Navigate to wikipedia.org and find the featured article of the day&#xA;&#xA;Thought: &amp;#34;Okay, I will start by navigating to wikipedia.org. Then I will analyze the page content to find the featured article of the day. Finally, I will extract the text of the featured article and output it.&amp;#34;&#xA;Tool: &amp;#34;navigate&amp;#34;, Param: &amp;#34;https://www.wikipedia.org&amp;#34;&#xA;&#xA;Thought: &amp;#34;Now that I have navigated to the Wikipedia homepage, I need to identify the element containing the &amp;#39;Featured article&amp;#39;. I will use the &amp;#39;get_page_content&amp;#39; tool to get the HTML content of the page and analyze it to find the appropriate CSS selector.&amp;#34;,&#xA;Tool: &amp;#34;get_page_content&amp;#34;&#xA;&#xA;Thought: &amp;#34;The HTML content I retrieved is for the main wikipedia.org page, which is just a language selection portal. I need to navigate to the English Wikipedia to find the featured article. I will click the English link.&amp;#34;,&#xA;Tool: &amp;#34;click&amp;#34;, Param: &amp;#34;#js-link-box-en&amp;#34;&#xA;&#xA;Thought&amp;#34;: &amp;#34;Now that I have clicked the link, I need to wait for the page to load.&amp;#34;,&#xA;Tool: &amp;#34;wait_for_navigation&amp;#34;&#xA;&#xA;Thought: &amp;#34;Now that the English Wikipedia page has loaded, I need to find the &amp;#39;Featured article&amp;#39; section. I will use the &amp;#39;get_page_content&amp;#39; tool to get the HTML content of the page and analyze it to find the appropriate CSS selector.&amp;#34;,&#xA;Tool: &amp;#34;get_page_content&amp;#34;&#xA;&#xA;Thought&amp;#34;: &amp;#34;I have analyzed the page content and found that the &amp;#39;Featured article&amp;#39; is located within a div with the id &amp;#39;mp-tfa&amp;#39;. I will now use the &amp;#39;get_text&amp;#39; tool to extract the text from this element.&amp;#34;,&#xA;Tool: &amp;#34;get_text&amp;#34;, Param: &amp;#34;#mp-tfa&amp;#34;&#xA;&#xA;Thought: &amp;#34;I have successfully extracted the text of the featured article. Now I will output the result.&amp;#34;&#xA;&#xA;===== TASK SUMMARY =====&#xA;Task: Navigate to wikipedia.org and find the featured article of the day&#xA;Status: incomplete&#xA;Duration: 12.39 seconds&#xA;Input tokens: 68486&#xA;Output tokens: 827&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The token usage was noticeably higher than my &lt;code&gt;ToolAgent&lt;/code&gt; experiments. This was almost entirely due to &lt;code&gt;get_page_content&lt;/code&gt; which &lt;strong&gt;&lt;em&gt;was&lt;/em&gt;&lt;/strong&gt; pushing the entire HTML content of the page into the LLM.&lt;/p&gt;&#xA;&lt;p&gt;I had to fix that.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings-managing-context&#34;&gt;Learnings: Managing Context&lt;/h2&gt;&#xA;&lt;p&gt;As I said, my initial implementation of &lt;code&gt;get_page_content&lt;/code&gt; just returned the entire HTML content back into the LLM. This wasn&amp;rsquo;t good. Because the LLM was using the page content to decide what HTML elements it needed to interact with, I could take some steps to limit which HTML elements I returned.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-1-regex&#34;&gt;Step 1: Regex&lt;/h2&gt;&#xA;&lt;p&gt;I apologize up front that I even tried this, but I just wanted to see if I could get some quick wins. I asked Copilot to create an implementation of &lt;code&gt;get_page_content&lt;/code&gt; that filtered out any elements that were not: Form elements, Elements with IDs, Elements with Roles or Aria-Labels, and a small set of special class names (like .btn).&lt;/p&gt;&#xA;&lt;p&gt;The result was a much smaller content context, but there were issues. Of course there were issues! I lived through the &amp;ldquo;Use regex on HTML&amp;rdquo; era and it&amp;rsquo;s not good.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-2-playwright&#34;&gt;Step 2: Playwright&lt;/h2&gt;&#xA;&lt;p&gt;I was already using Playwright, so why not use it in a way similar to how we&amp;rsquo;d use BeautifulSoup? One well crafted prompt later, and Copilot had a very functional version.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: You&amp;rsquo;re absolutely right! Using Playwright directly to select elements gives us much better control and maintains the structure and order of the original page.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Even Copilot was relieved we moved away from regex.&lt;/p&gt;&#xA;&lt;p&gt;The Playwright-based version had some other benefits:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Elements are extracted in the same order they appear in the DOM, preserving visual hierarchy&lt;/li&gt;&#xA;&lt;li&gt;Filtering allowed me to only includes important attributes like id, class, role, etc&lt;/li&gt;&#xA;&lt;li&gt;Executing some code in the DOM allowed me to only include visible elements&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These helped the LLM make better decisions about the HTML elements it needed to interact with during the task. It also dramatically reduced the amount of input tokens sent to the LLM.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-3-greedy-llm&#34;&gt;Step 3: Greedy LLM&lt;/h2&gt;&#xA;&lt;p&gt;The last thing I noticed and corrected was the LLM&amp;rsquo;s desire to ask for the page content whenever it got confused with CSS selectors. And yes, it gets confused. The LLM would fetch the page content, even when the page had not changed. Token usage goes up, and for the most part, the LLM didn&amp;rsquo;t zero-in on the right CSS selector. It likely just started guessing.&lt;/p&gt;&#xA;&lt;p&gt;I added a &lt;code&gt;is_maybe_dirty&lt;/code&gt; check in &lt;code&gt;PageManager&lt;/code&gt; which was set to &lt;code&gt;false&lt;/code&gt; after any call to &lt;code&gt;get_page_content&lt;/code&gt;. I set &lt;code&gt;is_maybe_dirty&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; after: &lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, and &lt;code&gt;type_text&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If &lt;code&gt;is_maybe_dirty=false&lt;/code&gt; in &lt;code&gt;get_page_content&lt;/code&gt;, I return an error result of &amp;ldquo;Use previously extracted page content&amp;rdquo;. The LLM correctly interpreted the result and knew it should stick with the current HTML content it already had.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s an example run: &amp;ldquo;Use &amp;lsquo;&lt;a href=&#34;https://duckduckgo.com&#34;&gt;https://duckduckgo.com&lt;/a&gt;&amp;rsquo;, search for &amp;lsquo;python tutorial&amp;rsquo;, and return the title of the first result found&amp;rdquo; (yes, it&amp;rsquo;s too fast to read so use the scrubber)&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;If I keep pushing on &lt;code&gt;ToolAgent&lt;/code&gt; and &lt;code&gt;WebAgent&lt;/code&gt;, I run the risk of starting to build simple frameworks. That might not be the worst outcome, but wasn&amp;rsquo;t my intention when starting these projects.&lt;/p&gt;&#xA;&lt;p&gt;Instead, I want to explore some embedding and vector search projects. I&amp;rsquo;ve been using Simon Willison&amp;rsquo;s &lt;code&gt;llm&lt;/code&gt; library, and it already includes some support for creating embeddings. Both SQLite and DuckDB have support for vector search. Sounds like I&amp;rsquo;ll have a lot to keep me busy.&lt;/p&gt;&#xA;&lt;p&gt;I also want to begin using some local models. Again, &lt;code&gt;llm&lt;/code&gt; already support this, so I&amp;rsquo;m unblocked.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;My fifth post on LLMs as Agents. I just it&amp;rsquo;s a series now.&lt;/p&gt;&#xA;&lt;p&gt;After refactoring &lt;code&gt;ToolAgent&lt;/code&gt;, I decided to do the same with &lt;code&gt;WebAgent&lt;/code&gt;, building it on the &lt;code&gt;ToolAgent&lt;/code&gt; system. For more context and a refresher on &lt;code&gt;WebAgent&lt;/code&gt;, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;: I looked at ways to improve the agent outcomes by using better approaches to planning. I settled on using ReAct, with some Few-Shot prompts, and had the LLM do some Chain of Thought (CoT) output.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/&#34;&gt;Tools &amp;amp; Benchmarking&lt;/a&gt;: I refactored the &lt;code&gt;ToolAgent&lt;/code&gt; code into a reusable base class, added some additional mock tools, and even some basic benchmarking.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;webagent&#34;&gt;WebAgent&lt;/h2&gt;&#xA;&lt;p&gt;After refactoring &lt;code&gt;ToolAgent&lt;/code&gt;, I decided to see if I could build &lt;code&gt;WebAgent&lt;/code&gt; on top of &lt;code&gt;ToolAgent&lt;/code&gt;. I think it turned out pretty nicely. Again, Copilot helped too.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;WebAgent&lt;/code&gt; now derives from &lt;code&gt;ToolAgent&lt;/code&gt;, and &lt;code&gt;PageManager&lt;/code&gt; (the wrapper around Playwright for browser automation) became a &lt;code&gt;ToolProvider&lt;/code&gt;. All of the wrappers for Playwright automation became tools the agent could use. This worked better than I thought.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Starting task: Navigate to wikipedia.org and find the featured article of the day&#xA;&#xA;Thought: &amp;#34;Okay, I will start by navigating to wikipedia.org. Then I will analyze the page content to find the featured article of the day. Finally, I will extract the text of the featured article and output it.&amp;#34;&#xA;Tool: &amp;#34;navigate&amp;#34;, Param: &amp;#34;https://www.wikipedia.org&amp;#34;&#xA;&#xA;Thought: &amp;#34;Now that I have navigated to the Wikipedia homepage, I need to identify the element containing the &amp;#39;Featured article&amp;#39;. I will use the &amp;#39;get_page_content&amp;#39; tool to get the HTML content of the page and analyze it to find the appropriate CSS selector.&amp;#34;,&#xA;Tool: &amp;#34;get_page_content&amp;#34;&#xA;&#xA;Thought: &amp;#34;The HTML content I retrieved is for the main wikipedia.org page, which is just a language selection portal. I need to navigate to the English Wikipedia to find the featured article. I will click the English link.&amp;#34;,&#xA;Tool: &amp;#34;click&amp;#34;, Param: &amp;#34;#js-link-box-en&amp;#34;&#xA;&#xA;Thought&amp;#34;: &amp;#34;Now that I have clicked the link, I need to wait for the page to load.&amp;#34;,&#xA;Tool: &amp;#34;wait_for_navigation&amp;#34;&#xA;&#xA;Thought: &amp;#34;Now that the English Wikipedia page has loaded, I need to find the &amp;#39;Featured article&amp;#39; section. I will use the &amp;#39;get_page_content&amp;#39; tool to get the HTML content of the page and analyze it to find the appropriate CSS selector.&amp;#34;,&#xA;Tool: &amp;#34;get_page_content&amp;#34;&#xA;&#xA;Thought&amp;#34;: &amp;#34;I have analyzed the page content and found that the &amp;#39;Featured article&amp;#39; is located within a div with the id &amp;#39;mp-tfa&amp;#39;. I will now use the &amp;#39;get_text&amp;#39; tool to extract the text from this element.&amp;#34;,&#xA;Tool: &amp;#34;get_text&amp;#34;, Param: &amp;#34;#mp-tfa&amp;#34;&#xA;&#xA;Thought: &amp;#34;I have successfully extracted the text of the featured article. Now I will output the result.&amp;#34;&#xA;&#xA;===== TASK SUMMARY =====&#xA;Task: Navigate to wikipedia.org and find the featured article of the day&#xA;Status: incomplete&#xA;Duration: 12.39 seconds&#xA;Input tokens: 68486&#xA;Output tokens: 827&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The token usage was noticeably higher than my &lt;code&gt;ToolAgent&lt;/code&gt; experiments. This was almost entirely due to &lt;code&gt;get_page_content&lt;/code&gt; which &lt;strong&gt;&lt;em&gt;was&lt;/em&gt;&lt;/strong&gt; pushing the entire HTML content of the page into the LLM.&lt;/p&gt;&#xA;&lt;p&gt;I had to fix that.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings-managing-context&#34;&gt;Learnings: Managing Context&lt;/h2&gt;&#xA;&lt;p&gt;As I said, my initial implementation of &lt;code&gt;get_page_content&lt;/code&gt; just returned the entire HTML content back into the LLM. This wasn&amp;rsquo;t good. Because the LLM was using the page content to decide what HTML elements it needed to interact with, I could take some steps to limit which HTML elements I returned.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-1-regex&#34;&gt;Step 1: Regex&lt;/h2&gt;&#xA;&lt;p&gt;I apologize up front that I even tried this, but I just wanted to see if I could get some quick wins. I asked Copilot to create an implementation of &lt;code&gt;get_page_content&lt;/code&gt; that filtered out any elements that were not: Form elements, Elements with IDs, Elements with Roles or Aria-Labels, and a small set of special class names (like .btn).&lt;/p&gt;&#xA;&lt;p&gt;The result was a much smaller content context, but there were issues. Of course there were issues! I lived through the &amp;ldquo;Use regex on HTML&amp;rdquo; era and it&amp;rsquo;s not good.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-2-playwright&#34;&gt;Step 2: Playwright&lt;/h2&gt;&#xA;&lt;p&gt;I was already using Playwright, so why not use it in a way similar to how we&amp;rsquo;d use BeautifulSoup? One well crafted prompt later, and Copilot had a very functional version.&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;&lt;strong&gt;Copilot&lt;/strong&gt;: You&amp;rsquo;re absolutely right! Using Playwright directly to select elements gives us much better control and maintains the structure and order of the original page.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Even Copilot was relieved we moved away from regex.&lt;/p&gt;&#xA;&lt;p&gt;The Playwright-based version had some other benefits:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Elements are extracted in the same order they appear in the DOM, preserving visual hierarchy&lt;/li&gt;&#xA;&lt;li&gt;Filtering allowed me to only includes important attributes like id, class, role, etc&lt;/li&gt;&#xA;&lt;li&gt;Executing some code in the DOM allowed me to only include visible elements&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;These helped the LLM make better decisions about the HTML elements it needed to interact with during the task. It also dramatically reduced the amount of input tokens sent to the LLM.&lt;/p&gt;&#xA;&lt;h2 id=&#34;step-3-greedy-llm&#34;&gt;Step 3: Greedy LLM&lt;/h2&gt;&#xA;&lt;p&gt;The last thing I noticed and corrected was the LLM&amp;rsquo;s desire to ask for the page content whenever it got confused with CSS selectors. And yes, it gets confused. The LLM would fetch the page content, even when the page had not changed. Token usage goes up, and for the most part, the LLM didn&amp;rsquo;t zero-in on the right CSS selector. It likely just started guessing.&lt;/p&gt;&#xA;&lt;p&gt;I added a &lt;code&gt;is_maybe_dirty&lt;/code&gt; check in &lt;code&gt;PageManager&lt;/code&gt; which was set to &lt;code&gt;false&lt;/code&gt; after any call to &lt;code&gt;get_page_content&lt;/code&gt;. I set &lt;code&gt;is_maybe_dirty&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; after: &lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;click&lt;/code&gt;, and &lt;code&gt;type_text&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;If &lt;code&gt;is_maybe_dirty=false&lt;/code&gt; in &lt;code&gt;get_page_content&lt;/code&gt;, I return an error result of &amp;ldquo;Use previously extracted page content&amp;rdquo;. The LLM correctly interpreted the result and knew it should stick with the current HTML content it already had.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s an example run: &amp;ldquo;Use &amp;lsquo;&lt;a href=&#34;https://duckduckgo.com&#34;&gt;https://duckduckgo.com&lt;/a&gt;&amp;rsquo;, search for &amp;lsquo;python tutorial&amp;rsquo;, and return the title of the first result found&amp;rdquo; (yes, it&amp;rsquo;s too fast to read so use the scrubber)&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;If I keep pushing on &lt;code&gt;ToolAgent&lt;/code&gt; and &lt;code&gt;WebAgent&lt;/code&gt;, I run the risk of starting to build simple frameworks. That might not be the worst outcome, but wasn&amp;rsquo;t my intention when starting these projects.&lt;/p&gt;&#xA;&lt;p&gt;Instead, I want to explore some embedding and vector search projects. I&amp;rsquo;ve been using Simon Willison&amp;rsquo;s &lt;code&gt;llm&lt;/code&gt; library, and it already includes some support for creating embeddings. Both SQLite and DuckDB have support for vector search. Sounds like I&amp;rsquo;ll have a lot to keep me busy.&lt;/p&gt;&#xA;&lt;p&gt;I also want to begin using some local models. Again, &lt;code&gt;llm&lt;/code&gt; already support this, so I&amp;rsquo;m unblocked.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Tools &amp;amp; Benchmarking</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/</link>
				<pubDate>Sun, 16 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-tools-benchmarking/</guid>
				<description>&lt;p&gt;I spent some time refactoring the Tool Agent code, added some additional mock tools and even some basic benchmarking. For more context, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;: I looked at ways to improve the agent outcomes by using better approaches to planning. I settled on using ReAct, with some Few-Shot prompts, and had the LLM do some Chain of Thought (CoT) output.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;code-refactor&#34;&gt;Code Refactor&lt;/h2&gt;&#xA;&lt;p&gt;The biggest change to the code is the somewhat large refactor to extract the agent-specific code into a &lt;code&gt;ToolAgent&lt;/code&gt; class and the tool-specific code into a set of &lt;code&gt;ToolProvider&lt;/code&gt; classes. &lt;code&gt;ToolProvider&lt;/code&gt; classes are registered with the &lt;code&gt;ToolAgent&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Register tool providers&#xA;agent = ToolAgent(model_name=&amp;#39;gemini-2.0-flash&amp;#39;)&#xA;agent.register_provider(UtilityToolProvider())&#xA;agent.register_provider(AppointmentToolProvider())&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ToolAgent&lt;/code&gt; can then be used in different ways. &lt;code&gt;tool_agent_test.py&lt;/code&gt; is a way to run the agent in a terminal window. I&amp;rsquo;ve been playing with using the agent in a chat system too, but more on that next time.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;python tool_agent_test.py&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;&#xA;&lt;p&gt;I wanted to keep adding more tools, which contributed to the code refactor. I couldn&amp;rsquo;t keep adding more code into the one Python file. With the new tools, I explored two new areas (for me):&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;LLM-based Tools&lt;/strong&gt;: A tool that was implemented using a secondary LLM. For a given set of topics, I wanted to &amp;ldquo;guess&amp;rdquo; which topics might be of interest to a user based on their input. LLMs are good at that stuff, so I made a simple tool call that used a separate system prompt. (I just realized that I didn&amp;rsquo;t add token tracking to that tool!)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Multi-Parameter Tool Calls&lt;/strong&gt;: Up until now, my tool calls took zero or one parameters. I wanted to start using multiple parameters, so I introduced an &lt;code&gt;object&lt;/code&gt; parameter type with a simple schema. In the future, I want the agent to provide feedback when required parameters are not provided.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;benchmarking&#34;&gt;Benchmarking&lt;/h2&gt;&#xA;&lt;p&gt;I mentioned in the last post that I needed to start doing some benchmarking on the agent. The code factor made it somewhat easy for Copilot (yes, I do some vibe coding too) to whip up a very basic benchmarking harness. Some details (more in the &lt;a href=&#34;https://github.com/mfinkle/llm-agents/blob/main/tool_agent.md&#34;&gt;readme&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Test Cases&lt;/strong&gt;: Run multiple predefined test cases&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Success Metrics&lt;/strong&gt;: Track success rates, execution times, and token usage&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Reporting&lt;/strong&gt;: Get detailed reports on test results&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;CSV/JSON Export&lt;/strong&gt;: Export results for further analysis&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Token Usage&lt;/strong&gt;: Track and analyze token consumption&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I added simple token tracking because I was running the benchmarking suite quite a bit and wanted to know what kind of money I was spending. Turns out, it was less than a penny a run, but it&amp;rsquo;s good to know.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;p&gt;I was running the benchmark suite trying to figure out why tests kept failing. The benchmarking framework measures success based on two metrics: (1) Did the agent use the minimum set of required tools? (2) Did the agent output a few key words in the final response. Yes, those aren&amp;rsquo;t the best success checks, but it&amp;rsquo;s what I have for now.&lt;/p&gt;&#xA;&lt;h3 id=&#34;more-jedi-prompt-tricks&#34;&gt;More Jedi Prompt Tricks&lt;/h3&gt;&#xA;&lt;p&gt;The agent was usually failing at using the required tools. Looking at the debug &lt;code&gt;[thought]&lt;/code&gt; output from the agent, I noticed that it would assume knowledge instead of using the tools. I found a &lt;a href=&#34;https://www.pinecone.io/learn/series/langchain/langchain-tools/&#34;&gt;blog post&lt;/a&gt; from Pinecone that talks about this when using LangChain too. They suggested adding explicit items to the system prompt. I already had something along the lines of &amp;ldquo;&lt;code&gt;ALWAYS use the tools. NEVER assume information&lt;/code&gt;&amp;rdquo; but that wasn&amp;rsquo;t enough. I added the following:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;IMPORTANT: Do not assume or guess or hallucinate information. Call the tools to get ALL needed information.&#xA;    - You DO NOT know the current date or time. Use tools to determine the current date and time.&#xA;    - You CANNOT do math. Use tools to calculate math.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;temperature&#34;&gt;Temperature&lt;/h3&gt;&#xA;&lt;p&gt;For the randomness on the output, I saw this note in the same Pinecone blog post:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Here we initialize the LLM with a temperature of 0. A low temperature is useful when using tools as it decreases the amount of “randomness” or “creativity” in the generated text of the LLMs, which is ideal for encouraging it to follow strict instructions — as required for tool usage.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Honestly, I had not played with any LLM options yet, but added the &lt;code&gt;temperature=0&lt;/code&gt; setting and it did help.&lt;/p&gt;&#xA;&lt;h3 id=&#34;more-json-output-handling&#34;&gt;More JSON Output Handling&lt;/h3&gt;&#xA;&lt;p&gt;I already had some code to handle the times when the LLM returned JSON using Markdown code fences, but there were times when the LLM would &lt;strong&gt;apologize for a mistake&lt;/strong&gt; and append the JSON. The results were correct, but my code failed to extract it! So I added more defensive measures to safely extract JSON from LLM responses. Yes, I should update to use LLM built-in support for JSON schemas at this point.&lt;/p&gt;&#xA;&lt;p&gt;With these changes, I regular had 100% success in my benchmarking runs! This means I need to make the tests harder.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to get a haircut tomorrow morning&#xA;&#xA;Thought: I need to find out the current date to determine what tomorrow is. I will use the get_datetime tool to get the current date and time.&#xA;Thought: The current date is 2025-03-15. I need to check for available appointments for a haircut. Since there is no haircut specialty, I should first check the available specialties.&#xA;Thought: Okay, &amp;#39;hair&amp;#39; is a valid specialty. I will now check for available &amp;#39;hair&amp;#39; appointments.&#xA;Thought: I have a list of available hair appointments. The user wants an appointment tomorrow morning, which is March 16th. There are two appointments available on March 16th: 11:00 AM and 2:00 PM. The user wants a morning appointment, so the 11:00 AM appointment is the best fit. I will ask the user if they want to book appointment ID 11.&#xA;&#xA;Agent: I see two appointments available for hair on March 16th: 11:00 AM and 2:00 PM. Would you like to book appointment ID 11 for 11:00 AM?&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;As I started to explore some non-chat user experiences, I discovered that I will need to agent to provide more structured output. Right now the output is very conversational, which works well for chat, but won&amp;rsquo;t be easy to use with other types of UX. I found some discussion on &lt;em&gt;taming&lt;/em&gt; the output of an LLM in this &lt;a href=&#34;https://tryolabs.com/blog/strategies-and-tools-for-controlling-responses&#34;&gt;blog post&lt;/a&gt; from Tryolabs.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve also started using &lt;code&gt;ToolAgent&lt;/code&gt; in my own chat server. I&amp;rsquo;m sure this branch of experimentation will continue as well.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I spent some time refactoring the Tool Agent code, added some additional mock tools and even some basic benchmarking. For more context, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/&#34;&gt;Planning via Prompting&lt;/a&gt;: I looked at ways to improve the agent outcomes by using better approaches to planning. I settled on using ReAct, with some Few-Shot prompts, and had the LLM do some Chain of Thought (CoT) output.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;code-refactor&#34;&gt;Code Refactor&lt;/h2&gt;&#xA;&lt;p&gt;The biggest change to the code is the somewhat large refactor to extract the agent-specific code into a &lt;code&gt;ToolAgent&lt;/code&gt; class and the tool-specific code into a set of &lt;code&gt;ToolProvider&lt;/code&gt; classes. &lt;code&gt;ToolProvider&lt;/code&gt; classes are registered with the &lt;code&gt;ToolAgent&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;# Register tool providers&#xA;agent = ToolAgent(model_name=&amp;#39;gemini-2.0-flash&amp;#39;)&#xA;agent.register_provider(UtilityToolProvider())&#xA;agent.register_provider(AppointmentToolProvider())&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ToolAgent&lt;/code&gt; can then be used in different ways. &lt;code&gt;tool_agent_test.py&lt;/code&gt; is a way to run the agent in a terminal window. I&amp;rsquo;ve been playing with using the agent in a chat system too, but more on that next time.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;python tool_agent_test.py&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;tools&#34;&gt;Tools&lt;/h2&gt;&#xA;&lt;p&gt;I wanted to keep adding more tools, which contributed to the code refactor. I couldn&amp;rsquo;t keep adding more code into the one Python file. With the new tools, I explored two new areas (for me):&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;LLM-based Tools&lt;/strong&gt;: A tool that was implemented using a secondary LLM. For a given set of topics, I wanted to &amp;ldquo;guess&amp;rdquo; which topics might be of interest to a user based on their input. LLMs are good at that stuff, so I made a simple tool call that used a separate system prompt. (I just realized that I didn&amp;rsquo;t add token tracking to that tool!)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Multi-Parameter Tool Calls&lt;/strong&gt;: Up until now, my tool calls took zero or one parameters. I wanted to start using multiple parameters, so I introduced an &lt;code&gt;object&lt;/code&gt; parameter type with a simple schema. In the future, I want the agent to provide feedback when required parameters are not provided.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;benchmarking&#34;&gt;Benchmarking&lt;/h2&gt;&#xA;&lt;p&gt;I mentioned in the last post that I needed to start doing some benchmarking on the agent. The code factor made it somewhat easy for Copilot (yes, I do some vibe coding too) to whip up a very basic benchmarking harness. Some details (more in the &lt;a href=&#34;https://github.com/mfinkle/llm-agents/blob/main/tool_agent.md&#34;&gt;readme&lt;/a&gt;):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Test Cases&lt;/strong&gt;: Run multiple predefined test cases&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Success Metrics&lt;/strong&gt;: Track success rates, execution times, and token usage&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Reporting&lt;/strong&gt;: Get detailed reports on test results&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;CSV/JSON Export&lt;/strong&gt;: Export results for further analysis&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Token Usage&lt;/strong&gt;: Track and analyze token consumption&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I added simple token tracking because I was running the benchmarking suite quite a bit and wanted to know what kind of money I was spending. Turns out, it was less than a penny a run, but it&amp;rsquo;s good to know.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;p&gt;I was running the benchmark suite trying to figure out why tests kept failing. The benchmarking framework measures success based on two metrics: (1) Did the agent use the minimum set of required tools? (2) Did the agent output a few key words in the final response. Yes, those aren&amp;rsquo;t the best success checks, but it&amp;rsquo;s what I have for now.&lt;/p&gt;&#xA;&lt;h3 id=&#34;more-jedi-prompt-tricks&#34;&gt;More Jedi Prompt Tricks&lt;/h3&gt;&#xA;&lt;p&gt;The agent was usually failing at using the required tools. Looking at the debug &lt;code&gt;[thought]&lt;/code&gt; output from the agent, I noticed that it would assume knowledge instead of using the tools. I found a &lt;a href=&#34;https://www.pinecone.io/learn/series/langchain/langchain-tools/&#34;&gt;blog post&lt;/a&gt; from Pinecone that talks about this when using LangChain too. They suggested adding explicit items to the system prompt. I already had something along the lines of &amp;ldquo;&lt;code&gt;ALWAYS use the tools. NEVER assume information&lt;/code&gt;&amp;rdquo; but that wasn&amp;rsquo;t enough. I added the following:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;IMPORTANT: Do not assume or guess or hallucinate information. Call the tools to get ALL needed information.&#xA;    - You DO NOT know the current date or time. Use tools to determine the current date and time.&#xA;    - You CANNOT do math. Use tools to calculate math.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;temperature&#34;&gt;Temperature&lt;/h3&gt;&#xA;&lt;p&gt;For the randomness on the output, I saw this note in the same Pinecone blog post:&lt;/p&gt;&#xA;&lt;p&gt;&lt;em&gt;Here we initialize the LLM with a temperature of 0. A low temperature is useful when using tools as it decreases the amount of “randomness” or “creativity” in the generated text of the LLMs, which is ideal for encouraging it to follow strict instructions — as required for tool usage.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Honestly, I had not played with any LLM options yet, but added the &lt;code&gt;temperature=0&lt;/code&gt; setting and it did help.&lt;/p&gt;&#xA;&lt;h3 id=&#34;more-json-output-handling&#34;&gt;More JSON Output Handling&lt;/h3&gt;&#xA;&lt;p&gt;I already had some code to handle the times when the LLM returned JSON using Markdown code fences, but there were times when the LLM would &lt;strong&gt;apologize for a mistake&lt;/strong&gt; and append the JSON. The results were correct, but my code failed to extract it! So I added more defensive measures to safely extract JSON from LLM responses. Yes, I should update to use LLM built-in support for JSON schemas at this point.&lt;/p&gt;&#xA;&lt;p&gt;With these changes, I regular had 100% success in my benchmarking runs! This means I need to make the tests harder.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to get a haircut tomorrow morning&#xA;&#xA;Thought: I need to find out the current date to determine what tomorrow is. I will use the get_datetime tool to get the current date and time.&#xA;Thought: The current date is 2025-03-15. I need to check for available appointments for a haircut. Since there is no haircut specialty, I should first check the available specialties.&#xA;Thought: Okay, &amp;#39;hair&amp;#39; is a valid specialty. I will now check for available &amp;#39;hair&amp;#39; appointments.&#xA;Thought: I have a list of available hair appointments. The user wants an appointment tomorrow morning, which is March 16th. There are two appointments available on March 16th: 11:00 AM and 2:00 PM. The user wants a morning appointment, so the 11:00 AM appointment is the best fit. I will ask the user if they want to book appointment ID 11.&#xA;&#xA;Agent: I see two appointments available for hair on March 16th: 11:00 AM and 2:00 PM. Would you like to book appointment ID 11 for 11:00 AM?&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next&lt;/h2&gt;&#xA;&lt;p&gt;As I started to explore some non-chat user experiences, I discovered that I will need to agent to provide more structured output. Right now the output is very conversational, which works well for chat, but won&amp;rsquo;t be easy to use with other types of UX. I found some discussion on &lt;em&gt;taming&lt;/em&gt; the output of an LLM in this &lt;a href=&#34;https://tryolabs.com/blog/strategies-and-tools-for-controlling-responses&#34;&gt;blog post&lt;/a&gt; from Tryolabs.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve also started using &lt;code&gt;ToolAgent&lt;/code&gt; in my own chat server. I&amp;rsquo;m sure this branch of experimentation will continue as well.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Planning via Prompting</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/</link>
				<pubDate>Sun, 09 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-planning-via-prompting/</guid>
				<description>&lt;p&gt;My exploration of how to use LLMs as Agents continues. In this post, I&amp;rsquo;m looking at ways to improve the outcomes by using better approaches to planning. For more context, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;planning--reasoning&#34;&gt;Planning &amp;amp; Reasoning&lt;/h2&gt;&#xA;&lt;p&gt;I have seen a lot of people talking about &amp;ldquo;planning&amp;rdquo; and &amp;ldquo;reasoning&amp;rdquo; as crucial parts of an LLM agent system, so I started to read and learn more about different strategies. There is obviously a ton written about LLMs and Agents all over the internet, but I stumbled upon a useful web site that collects a lot of the information and has references to where the ideas originated.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/&#34;&gt;Prompt Engineering Guide&lt;/a&gt;, not surprisingly, focuses on many different prompting strategies. It turns out, most of the different planning and reasoning strategies &lt;strong&gt;&lt;em&gt;are based on&lt;/em&gt;&lt;/strong&gt; different prompting strategies. Here are some of the strategies I progressed through:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/zeroshot&#34;&gt;Zero-shot&lt;/a&gt;: I think of this strategy as &amp;ldquo;hoping the LLM was trained well enough to just know the answer&amp;rdquo;. &lt;strong&gt;Hope is not a strategy&lt;/strong&gt;, but you&amp;rsquo;d be surprised how many people believe the LLM can just figure everything out on its own.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/fewshot&#34;&gt;Few-shot&lt;/a&gt;: This strategy enables in-context learning and depends on adding some examples to your prompt to help guide the model to better outcomes. My initial &lt;code&gt;tool_agent&lt;/code&gt; used this approach before I even knew what it was called.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/react&#34;&gt;ReAct&lt;/a&gt; (Reasoning + Acting): This strategy combines &lt;em&gt;reasoning&lt;/em&gt; and &lt;em&gt;acting&lt;/em&gt; with LLMs into the prompt. ReAct prompts LLMs to generate reasoning and actions for a task. Asking the LLM to &amp;ldquo;think about the step&amp;rdquo; is enough to create significant improvements to the outcomes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Funny thing&lt;/strong&gt;: I thought I wanted to try using something called &lt;a href=&#34;https://www.promptingguide.ai/techniques/cot&#34;&gt;Chain of Thought&lt;/a&gt; (CoT), which is another prompt strategy to get the LLM to think about the steps. CoT ended up being a pain to implement in a simple way. I ended up wanting to merge the &amp;ldquo;thought&amp;rdquo; (reasoning) and &amp;ldquo;action&amp;rdquo; parts together into the same response — which turns out to basically be ReAct.&lt;/p&gt;&#xA;&lt;h2 id=&#34;example-flows&#34;&gt;Example Flows&lt;/h2&gt;&#xA;&lt;p&gt;I honestly don&amp;rsquo;t know if the ReAct strategy is yielding better outcomes than the Few-shot approach I was using. I guess I need to set up benchmarks next? The flows are promising though, and the ReAct approach didn&amp;rsquo;t add much to the size of the code.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: What&amp;#39;s the weather in Beverly Hills?&#xA;&#xA;Thought: The user wants to know the weather in Beverly Hills. First, I need to get the zipcode for Beverly Hills, then I can use that to get the weather.&#xA;Thought: Now that I have the zipcode for Beverly Hills, I can get the weather.&#xA;Thought: I have the weather information. Now I need to present it to the user.&#xA;&#xA;Agent: The weather in Beverly Hills is 75 F and Sunny.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s a longer one that uses the appointment scheduling tools:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to book a haircut&#xA;&#xA;Thought: The user wants to book a haircut. I need to find the appropriate specialty to book an appointment for. I should first check the available specialties.&#xA;Thought: The available specialties are hair, dentist, and vision. Since the user wants a haircut, the relevant specialty is &amp;#39;hair&amp;#39;. Now I need to find available appointments for &amp;#39;hair&amp;#39;.&#xA;Thought: I have found the available appointments for a haircut. Now I need to present them to the user and ask them to choose one.&#xA;&#xA;Agent: Here are the available appointments for a haircut:&#xA;&#xA;* ID: 10, Date: 2025-03-09, Time: 10:30 AM&#xA;* ID: 11, Date: 2025-03-10, Time: 2:00 PM&#xA;* ID: 12, Date: 2025-03-16, Time: 11:00 AM&#xA;* ID: 13, Date: 2025-03-16, Time: 3:00 PM&#xA;&#xA;Please tell me the ID of the appointment you would like to book.&#xA;&#xA;You: 12&#xA;&#xA;Thought: The user has selected appointment ID 12. Now I need to book this appointment.&#xA;Thought: The appointment has been successfully booked. I need to inform the user.&#xA;&#xA;Agent: Great! Your appointment has been booked successfully for March 16, 2025, at 11:00 AM. The appointment ID is 12.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;h3 id=&#34;reflection-and-correction&#34;&gt;Reflection and Correction&lt;/h3&gt;&#xA;&lt;p&gt;There are others ways we can improve the outcomes of the LLM agent beyond planning and reasoning. Reflection and correction is a way to help the model learn from its mistakes. I already had some basic checks in place to catch invalid JSON and unknown tools, but I made some additions to the code to help:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Validation of the JSON actions for expected fields&lt;/li&gt;&#xA;&lt;li&gt;Validation loops to tell the LLM about any validation failures and allow the LLM to try again.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;prompt--tool-consistency&#34;&gt;Prompt &amp;amp; Tool Consistency&lt;/h3&gt;&#xA;&lt;p&gt;I also realized, once again, that I was confusing the LLM with some of the ways I framed the prompt and tools.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I referred to both &amp;ldquo;tools&amp;rdquo; and &amp;ldquo;functions&amp;rdquo; in the initial prompt and subsequent conversational prompts. I decided to only use &amp;ldquo;tool&amp;rdquo; and changed all &amp;ldquo;function&amp;rdquo; references the LLM saw to &amp;ldquo;tool&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;I made some of the tool names ambiguous and the LLM would try to use tools that were not appropriate. An easy one is &lt;code&gt;get_specialties&lt;/code&gt; which returns the list of specialties used for appointment booking. The LLM would sometimes avoid using the tool to figure out what types of appointments could be booked and would try other tools or guess. I changed the tool name to &lt;code&gt;get_appointment_specialties&lt;/code&gt; and the LLM always used it correctly.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;I want to add more tools. I want to add tools that use secondary LLMs and I want to add tools that allow me to use the Agent for my own tasks.&lt;/p&gt;&#xA;&lt;p&gt;I want to explore different UX around agents. Chat is an obvious choice, and I will likely expose &lt;code&gt;tool_agent&lt;/code&gt; into a simple Web Chat system to play with it more and try to use it for actual tasks.&lt;/p&gt;&#xA;&lt;p&gt;As mentioned above, I may look into benchmarks too.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;My exploration of how to use LLMs as Agents continues. In this post, I&amp;rsquo;m looking at ways to improve the outcomes by using better approaches to planning. For more context, checkout the previous posts:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;Minimalist Approach&lt;/a&gt;: I kicked off my exploration by making two agents using the bare-minimum dependencies. I wanted to learn the concepts, before using a do-it-all framework.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/&#34;&gt;Taking Action&lt;/a&gt;: I decided to focus on the &lt;code&gt;tool_agent&lt;/code&gt; and add some read/write tools for the agent to use. I also discovered the need to send clear and consistent context in prompts.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Take a look at the &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;repository&lt;/a&gt; to see the code.&lt;/p&gt;&#xA;&lt;h2 id=&#34;planning--reasoning&#34;&gt;Planning &amp;amp; Reasoning&lt;/h2&gt;&#xA;&lt;p&gt;I have seen a lot of people talking about &amp;ldquo;planning&amp;rdquo; and &amp;ldquo;reasoning&amp;rdquo; as crucial parts of an LLM agent system, so I started to read and learn more about different strategies. There is obviously a ton written about LLMs and Agents all over the internet, but I stumbled upon a useful web site that collects a lot of the information and has references to where the ideas originated.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/&#34;&gt;Prompt Engineering Guide&lt;/a&gt;, not surprisingly, focuses on many different prompting strategies. It turns out, most of the different planning and reasoning strategies &lt;strong&gt;&lt;em&gt;are based on&lt;/em&gt;&lt;/strong&gt; different prompting strategies. Here are some of the strategies I progressed through:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/zeroshot&#34;&gt;Zero-shot&lt;/a&gt;: I think of this strategy as &amp;ldquo;hoping the LLM was trained well enough to just know the answer&amp;rdquo;. &lt;strong&gt;Hope is not a strategy&lt;/strong&gt;, but you&amp;rsquo;d be surprised how many people believe the LLM can just figure everything out on its own.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/fewshot&#34;&gt;Few-shot&lt;/a&gt;: This strategy enables in-context learning and depends on adding some examples to your prompt to help guide the model to better outcomes. My initial &lt;code&gt;tool_agent&lt;/code&gt; used this approach before I even knew what it was called.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.promptingguide.ai/techniques/react&#34;&gt;ReAct&lt;/a&gt; (Reasoning + Acting): This strategy combines &lt;em&gt;reasoning&lt;/em&gt; and &lt;em&gt;acting&lt;/em&gt; with LLMs into the prompt. ReAct prompts LLMs to generate reasoning and actions for a task. Asking the LLM to &amp;ldquo;think about the step&amp;rdquo; is enough to create significant improvements to the outcomes.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Funny thing&lt;/strong&gt;: I thought I wanted to try using something called &lt;a href=&#34;https://www.promptingguide.ai/techniques/cot&#34;&gt;Chain of Thought&lt;/a&gt; (CoT), which is another prompt strategy to get the LLM to think about the steps. CoT ended up being a pain to implement in a simple way. I ended up wanting to merge the &amp;ldquo;thought&amp;rdquo; (reasoning) and &amp;ldquo;action&amp;rdquo; parts together into the same response — which turns out to basically be ReAct.&lt;/p&gt;&#xA;&lt;h2 id=&#34;example-flows&#34;&gt;Example Flows&lt;/h2&gt;&#xA;&lt;p&gt;I honestly don&amp;rsquo;t know if the ReAct strategy is yielding better outcomes than the Few-shot approach I was using. I guess I need to set up benchmarks next? The flows are promising though, and the ReAct approach didn&amp;rsquo;t add much to the size of the code.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: What&amp;#39;s the weather in Beverly Hills?&#xA;&#xA;Thought: The user wants to know the weather in Beverly Hills. First, I need to get the zipcode for Beverly Hills, then I can use that to get the weather.&#xA;Thought: Now that I have the zipcode for Beverly Hills, I can get the weather.&#xA;Thought: I have the weather information. Now I need to present it to the user.&#xA;&#xA;Agent: The weather in Beverly Hills is 75 F and Sunny.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here&amp;rsquo;s a longer one that uses the appointment scheduling tools:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to book a haircut&#xA;&#xA;Thought: The user wants to book a haircut. I need to find the appropriate specialty to book an appointment for. I should first check the available specialties.&#xA;Thought: The available specialties are hair, dentist, and vision. Since the user wants a haircut, the relevant specialty is &amp;#39;hair&amp;#39;. Now I need to find available appointments for &amp;#39;hair&amp;#39;.&#xA;Thought: I have found the available appointments for a haircut. Now I need to present them to the user and ask them to choose one.&#xA;&#xA;Agent: Here are the available appointments for a haircut:&#xA;&#xA;* ID: 10, Date: 2025-03-09, Time: 10:30 AM&#xA;* ID: 11, Date: 2025-03-10, Time: 2:00 PM&#xA;* ID: 12, Date: 2025-03-16, Time: 11:00 AM&#xA;* ID: 13, Date: 2025-03-16, Time: 3:00 PM&#xA;&#xA;Please tell me the ID of the appointment you would like to book.&#xA;&#xA;You: 12&#xA;&#xA;Thought: The user has selected appointment ID 12. Now I need to book this appointment.&#xA;Thought: The appointment has been successfully booked. I need to inform the user.&#xA;&#xA;Agent: Great! Your appointment has been booked successfully for March 16, 2025, at 11:00 AM. The appointment ID is 12.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;&#xA;&lt;h3 id=&#34;reflection-and-correction&#34;&gt;Reflection and Correction&lt;/h3&gt;&#xA;&lt;p&gt;There are others ways we can improve the outcomes of the LLM agent beyond planning and reasoning. Reflection and correction is a way to help the model learn from its mistakes. I already had some basic checks in place to catch invalid JSON and unknown tools, but I made some additions to the code to help:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Validation of the JSON actions for expected fields&lt;/li&gt;&#xA;&lt;li&gt;Validation loops to tell the LLM about any validation failures and allow the LLM to try again.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;prompt--tool-consistency&#34;&gt;Prompt &amp;amp; Tool Consistency&lt;/h3&gt;&#xA;&lt;p&gt;I also realized, once again, that I was confusing the LLM with some of the ways I framed the prompt and tools.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;I referred to both &amp;ldquo;tools&amp;rdquo; and &amp;ldquo;functions&amp;rdquo; in the initial prompt and subsequent conversational prompts. I decided to only use &amp;ldquo;tool&amp;rdquo; and changed all &amp;ldquo;function&amp;rdquo; references the LLM saw to &amp;ldquo;tool&amp;rdquo;.&lt;/li&gt;&#xA;&lt;li&gt;I made some of the tool names ambiguous and the LLM would try to use tools that were not appropriate. An easy one is &lt;code&gt;get_specialties&lt;/code&gt; which returns the list of specialties used for appointment booking. The LLM would sometimes avoid using the tool to figure out what types of appointments could be booked and would try other tools or guess. I changed the tool name to &lt;code&gt;get_appointment_specialties&lt;/code&gt; and the LLM always used it correctly.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;I want to add more tools. I want to add tools that use secondary LLMs and I want to add tools that allow me to use the Agent for my own tasks.&lt;/p&gt;&#xA;&lt;p&gt;I want to explore different UX around agents. Chat is an obvious choice, and I will likely expose &lt;code&gt;tool_agent&lt;/code&gt; into a simple Web Chat system to play with it more and try to use it for actual tasks.&lt;/p&gt;&#xA;&lt;p&gt;As mentioned above, I may look into benchmarks too.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: Taking Action</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/</link>
				<pubDate>Sun, 02 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-taking-action/</guid>
				<description>&lt;p&gt;I&amp;rsquo;m still exploring how to use LLMs to build agents. In the &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;first post&lt;/a&gt;, I described some of the motivation and approach. I started working on two agents:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;web_agent&lt;/strong&gt;: A basic agent that completes a web-based task using browser automation actions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;tool_agent&lt;/strong&gt;: A basic agent that completes a task using a set of supplied tools or methods.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve been focused more on tool_agent, trying to expand the types of tools I can provide. I had a suggestion to add some read/write type tools and scenarios, so I decided to try that out.&lt;/p&gt;&#xA;&lt;h2 id=&#34;taking-action&#34;&gt;Taking Action&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;an agent is characterized by the &lt;em&gt;&lt;strong&gt;environment&lt;/strong&gt;&lt;/em&gt; it operates in and &lt;em&gt;&lt;strong&gt;the set of actions&lt;/strong&gt;&lt;/em&gt; it can perform. — Chip Huyen &lt;a href=&#34;https://huyenchip.com/2025/01/07/agents.html&#34;&gt;Agents&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I want to create a more realistic set of actions for the agent to use. Scheduling appointments seemed like a straightforward scenario with some interesting possible flows. I added a few tool methods and a mocked appointment data store.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;get_specialties()&lt;/code&gt;: Retrieves a list of available specialties for scheduling appointments.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;get_available_appointments(specialty)&lt;/code&gt;: Retrieves available appointments for a given specialty.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;book_appointment(booking_data)&lt;/code&gt;: Books an appointment based on provided booking data. The agent can pass in an appointment ID or a combination of a date and time.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;get_my_appointments()&lt;/code&gt;: Retrieves the list of appointments booked by the user.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cancel_appointment(appointment_id)&lt;/code&gt;: Cancels a booked appointment based on the provided appointment ID.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Checkout the &lt;a href=&#34;https://github.com/mfinkle/llm-agents/blob/main/tool_agent.py&#34;&gt;&lt;code&gt;tool_agent.py&lt;/code&gt;&lt;/a&gt; code for more detail on the way the tools work and the way the tools are added to the system prompt.&lt;/p&gt;&#xA;&lt;h2 id=&#34;example-flows&#34;&gt;Example Flows&lt;/h2&gt;&#xA;&lt;p&gt;I was pleased with how well this rudimentary system worked. Without many tweaks (some on that below), I was able to get some useful exchanges.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: what kind of appts can I book?&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_specialties&amp;#34;}&#xA;Function result: [&amp;#34;dentist&amp;#34;, &amp;#34;vision&amp;#34;, &amp;#34;hair&amp;#34;]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;You can book appointments for dentist, vision, and hair.&amp;#34;}&#xA;Agent: You can book appointments for dentist, vision, and hair.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The LLM did a good job of taking some ambiguity (&amp;ldquo;appts&amp;rdquo; and &amp;ldquo;book&amp;rdquo;) to figure out that it needed to use &lt;code&gt;get_specialties&lt;/code&gt; to answer the question.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to get a haircut&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_specialties&amp;#34;}&#xA;Function result: [&amp;#34;vision&amp;#34;, &amp;#34;dentist&amp;#34;, &amp;#34;hair&amp;#34;]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_available_appointments&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;hair&amp;#34;}&#xA;Function result: [... list of appointments ...]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Okay, here are the available hair appointments:&#xA;&#xA;* ID: 10, Date: 2025-03-01, Time: 10:30 AM&#xA;* ID: 11, Date: 2025-03-02, Time: 2:00 PM&#xA;* ID: 12, Date: 2025-03-08, Time: 11:00 AM&#xA;* ID: 13, Date: 2025-03-08, Time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this exchange, the agent was able to figure out that it needed to display &amp;ldquo;hair&amp;rdquo; appointments. &lt;strong&gt;Interesting&lt;/strong&gt;: the agent came up with the output display format on its own. When I saw the agent using this format, I added the ability to reference appointments by ID in the tool methods.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: show me my appointments&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_my_appointments&amp;#34;}&#xA;Function result: [{&amp;#34;id&amp;#34;: &amp;#34;3&amp;#34;, &amp;#34;date&amp;#34;: &amp;#34;2025-03-02&amp;#34;, &amp;#34;time&amp;#34;: &amp;#34;11:00 AM&amp;#34;, &amp;#34;specialty&amp;#34;: &amp;#34;dentist&amp;#34;}]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Here are your booked appointments:&#xA;&#xA;* ID: 3, Date: 2025-03-02, Time: 11:00 AM, Specialty: dentist&#xA;&#xA;You: cancel the dentist appointment&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;cancel_appointment&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;3&amp;#34;}&#xA;Function result: {&amp;#34;status&amp;#34;: &amp;#34;success&amp;#34;, &amp;#34;message&amp;#34;: &amp;#34;Appointment canceled successfully.&amp;#34;}&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Your appointment has been canceled successfully.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I thought I could easily confuse the agent by trying to cancel an appointment by the type (specialty) of the appointment, but the agent used the correct ID for the appointment when cancelling via the tool method.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learning&#34;&gt;Learning&lt;/h2&gt;&#xA;&lt;p&gt;At one point, the agent started returning invalid JSON. I assumed I need to be more strict in my system prompt, but I discovered an error. Since I was surrounding my Python strings with double quotes, the inner quotes were single quotes. My JSON examples were using single quotes — which is illegal in JSON. The LLM was relying on those examples, so I was causing the LLM to emit invalid JSON.&lt;/p&gt;&#xA;&lt;p&gt;As I was looking at the code for similar issues, I noticed I was sending Python formatted object output back into the conversation. The LLM was handling it well enough but I decided to change the output to be legal JSON output:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;- response.conversation.prompt(f&amp;#34;Function result: {function_result}&amp;#34;)&#xA;+  function_result_json = json.dumps(function_result)&#xA;+  response = conversation.prompt(f&amp;#34;Function result: {function_result_json}&amp;#34;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;Most of Chip Huyen&amp;rsquo;s post on &lt;a href=&#34;https://huyenchip.com/2025/01/07/agents.html&#34;&gt;Agents&lt;/a&gt; talks about &amp;ldquo;planning&amp;rdquo;, but I have not really adding any planning specific code to &lt;code&gt;tool_agent&lt;/code&gt; yet. Right now, I am getting by with whatever amount of planning the LLM can create itself.&lt;/p&gt;&#xA;&lt;p&gt;I want to learn more about planning, and how to add a little code to help the agent deal with even more complicated scenarios.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I&amp;rsquo;m still exploring how to use LLMs to build agents. In the &lt;a href=&#34;https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/&#34;&gt;first post&lt;/a&gt;, I described some of the motivation and approach. I started working on two agents:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;web_agent&lt;/strong&gt;: A basic agent that completes a web-based task using browser automation actions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;tool_agent&lt;/strong&gt;: A basic agent that completes a task using a set of supplied tools or methods.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve been focused more on tool_agent, trying to expand the types of tools I can provide. I had a suggestion to add some read/write type tools and scenarios, so I decided to try that out.&lt;/p&gt;&#xA;&lt;h2 id=&#34;taking-action&#34;&gt;Taking Action&lt;/h2&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;an agent is characterized by the &lt;em&gt;&lt;strong&gt;environment&lt;/strong&gt;&lt;/em&gt; it operates in and &lt;em&gt;&lt;strong&gt;the set of actions&lt;/strong&gt;&lt;/em&gt; it can perform. — Chip Huyen &lt;a href=&#34;https://huyenchip.com/2025/01/07/agents.html&#34;&gt;Agents&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I want to create a more realistic set of actions for the agent to use. Scheduling appointments seemed like a straightforward scenario with some interesting possible flows. I added a few tool methods and a mocked appointment data store.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;get_specialties()&lt;/code&gt;: Retrieves a list of available specialties for scheduling appointments.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;get_available_appointments(specialty)&lt;/code&gt;: Retrieves available appointments for a given specialty.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;book_appointment(booking_data)&lt;/code&gt;: Books an appointment based on provided booking data. The agent can pass in an appointment ID or a combination of a date and time.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;get_my_appointments()&lt;/code&gt;: Retrieves the list of appointments booked by the user.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;cancel_appointment(appointment_id)&lt;/code&gt;: Cancels a booked appointment based on the provided appointment ID.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Checkout the &lt;a href=&#34;https://github.com/mfinkle/llm-agents/blob/main/tool_agent.py&#34;&gt;&lt;code&gt;tool_agent.py&lt;/code&gt;&lt;/a&gt; code for more detail on the way the tools work and the way the tools are added to the system prompt.&lt;/p&gt;&#xA;&lt;h2 id=&#34;example-flows&#34;&gt;Example Flows&lt;/h2&gt;&#xA;&lt;p&gt;I was pleased with how well this rudimentary system worked. Without many tweaks (some on that below), I was able to get some useful exchanges.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: what kind of appts can I book?&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_specialties&amp;#34;}&#xA;Function result: [&amp;#34;dentist&amp;#34;, &amp;#34;vision&amp;#34;, &amp;#34;hair&amp;#34;]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;You can book appointments for dentist, vision, and hair.&amp;#34;}&#xA;Agent: You can book appointments for dentist, vision, and hair.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The LLM did a good job of taking some ambiguity (&amp;ldquo;appts&amp;rdquo; and &amp;ldquo;book&amp;rdquo;) to figure out that it needed to use &lt;code&gt;get_specialties&lt;/code&gt; to answer the question.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: I want to get a haircut&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_specialties&amp;#34;}&#xA;Function result: [&amp;#34;vision&amp;#34;, &amp;#34;dentist&amp;#34;, &amp;#34;hair&amp;#34;]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_available_appointments&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;hair&amp;#34;}&#xA;Function result: [... list of appointments ...]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Okay, here are the available hair appointments:&#xA;&#xA;* ID: 10, Date: 2025-03-01, Time: 10:30 AM&#xA;* ID: 11, Date: 2025-03-02, Time: 2:00 PM&#xA;* ID: 12, Date: 2025-03-08, Time: 11:00 AM&#xA;* ID: 13, Date: 2025-03-08, Time: 3:00 PM&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In this exchange, the agent was able to figure out that it needed to display &amp;ldquo;hair&amp;rdquo; appointments. &lt;strong&gt;Interesting&lt;/strong&gt;: the agent came up with the output display format on its own. When I saw the agent using this format, I added the ability to reference appointments by ID in the tool methods.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: show me my appointments&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;get_my_appointments&amp;#34;}&#xA;Function result: [{&amp;#34;id&amp;#34;: &amp;#34;3&amp;#34;, &amp;#34;date&amp;#34;: &amp;#34;2025-03-02&amp;#34;, &amp;#34;time&amp;#34;: &amp;#34;11:00 AM&amp;#34;, &amp;#34;specialty&amp;#34;: &amp;#34;dentist&amp;#34;}]&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Here are your booked appointments:&#xA;&#xA;* ID: 3, Date: 2025-03-02, Time: 11:00 AM, Specialty: dentist&#xA;&#xA;You: cancel the dentist appointment&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;call_function&amp;#34;, &amp;#34;tool&amp;#34;: &amp;#34;cancel_appointment&amp;#34;, &amp;#34;param&amp;#34;: &amp;#34;3&amp;#34;}&#xA;Function result: {&amp;#34;status&amp;#34;: &amp;#34;success&amp;#34;, &amp;#34;message&amp;#34;: &amp;#34;Appointment canceled successfully.&amp;#34;}&#xA;Action raw: {&amp;#34;type&amp;#34;: &amp;#34;output&amp;#34;, &amp;#34;value&amp;#34;: &amp;#34;... output ...&amp;#34;}&#xA;Agent: Your appointment has been canceled successfully.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I thought I could easily confuse the agent by trying to cancel an appointment by the type (specialty) of the appointment, but the agent used the correct ID for the appointment when cancelling via the tool method.&lt;/p&gt;&#xA;&lt;h2 id=&#34;learning&#34;&gt;Learning&lt;/h2&gt;&#xA;&lt;p&gt;At one point, the agent started returning invalid JSON. I assumed I need to be more strict in my system prompt, but I discovered an error. Since I was surrounding my Python strings with double quotes, the inner quotes were single quotes. My JSON examples were using single quotes — which is illegal in JSON. The LLM was relying on those examples, so I was causing the LLM to emit invalid JSON.&lt;/p&gt;&#xA;&lt;p&gt;As I was looking at the code for similar issues, I noticed I was sending Python formatted object output back into the conversation. The LLM was handling it well enough but I decided to change the output to be legal JSON output:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;- response.conversation.prompt(f&amp;#34;Function result: {function_result}&amp;#34;)&#xA;+  function_result_json = json.dumps(function_result)&#xA;+  response = conversation.prompt(f&amp;#34;Function result: {function_result_json}&amp;#34;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;&#xA;&lt;p&gt;Most of Chip Huyen&amp;rsquo;s post on &lt;a href=&#34;https://huyenchip.com/2025/01/07/agents.html&#34;&gt;Agents&lt;/a&gt; talks about &amp;ldquo;planning&amp;rdquo;, but I have not really adding any planning specific code to &lt;code&gt;tool_agent&lt;/code&gt; yet. Right now, I am getting by with whatever amount of planning the LLM can create itself.&lt;/p&gt;&#xA;&lt;p&gt;I want to learn more about planning, and how to add a little code to help the agent deal with even more complicated scenarios.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Exploring LLMs as Agents: A Minimalist Approach</title>
				<link>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/</link>
				<pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2025/03/exploring-llms-as-agents-a-minimalist-approach/</guid>
				<description>&lt;p&gt;Large Language Models (LLMs) are powerful tools for generating text, answering questions, and coding. We&amp;rsquo;ve moved beyond generating content, and LLMs are now being used to take actions as &lt;strong&gt;agents&lt;/strong&gt; — independent entities that can act, use tools, and interact with their environment. You probably already know all of this.&lt;/p&gt;&#xA;&lt;p&gt;I wanted to explore using LLMs as agents, but I like to get an understanding of the underlying components before using high-level frameworks to hide all of the minutiae and make the process of building production-ready systems. Understanding how the different components work and interact is important to my own learning process.&lt;/p&gt;&#xA;&lt;p&gt;That’s exactly what my &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;&lt;strong&gt;LLM Agents&lt;/strong&gt; project&lt;/a&gt; sets out to do. Instead of relying on frameworks that abstract away the details, the project takes a &lt;strong&gt;bare-bones approach&lt;/strong&gt; to learning how LLMs can function as agents. By minimizing dependencies, I can get a clearer understanding of the challenges, possibilities, and mechanics of building LLM-powered agents.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-minimal-dependencies-matter-to-me&#34;&gt;Why Minimal Dependencies Matter (To Me)&lt;/h2&gt;&#xA;&lt;p&gt;Many existing frameworks promise powerful LLM agent capabilities, but they often come at the cost of &lt;strong&gt;hiding the underlying complexities&lt;/strong&gt;. While these frameworks can be useful, starting with minimal dependencies allows us to:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Understand the fundamentals&lt;/strong&gt;: How does an LLM process information to take actions? How does the system prompt impact the effectiveness of the agent?&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Explore limitations&lt;/strong&gt;: What challenges arise when an agent tries to perform a multi-step task? How does the shape of the tools (functions or APIs) impact how the agent can process the flow.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Control the design&lt;/strong&gt;: Without being boxed into a framework’s way of doing things, we can experiment freely. We can then use this knowledge to help pick the right type of framework for more advanced and production use cases.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This project keeps things simple, using only a lightweight LLM library, Simon Willison&amp;rsquo;s great &lt;code&gt;llm&lt;/code&gt; Python library (&lt;a href=&#34;https://github.com/simonw/llm&#34;&gt;code&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;docs&lt;/a&gt;), and &lt;a href=&#34;https://playwright.dev&#34;&gt;Playwright&lt;/a&gt; for handling web automation.&lt;/p&gt;&#xA;&lt;p&gt;These agents are not production ready, but they are trimmed down enough to see the mechanisms at work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;meet-the-agents&#34;&gt;Meet the Agents&lt;/h2&gt;&#xA;&lt;p&gt;The repository contains &lt;strong&gt;two primary agents&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;h3 id=&#34;web-agent-navigating-the-web-with-llms&#34;&gt;Web Agent: Navigating the Web with LLMs&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;strong&gt;Web Agent&lt;/strong&gt; is designed to interact with websites using the &lt;a href=&#34;https://playwright.dev/python/docs/intro&#34;&gt;Playwright&lt;/a&gt; Python library. Instead of treating a webpage as structured data, this agent lets an LLM interpret raw HTML and decide what actions to take—whether that means clicking a button, typing into a form, or extracting text. I wanted to see how well an agent could navigate something as confusing as a modern website. If you&amp;rsquo;ve ever done a &amp;ldquo;view source&amp;rdquo; or &amp;ldquo;inspect&amp;rdquo; on a modern webpage, you know what I mean.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;PageManager&lt;/strong&gt; class handles the browser automation using Playwright.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The &lt;strong&gt;LLM generates the next action&lt;/strong&gt; based on the current page content and the assigned task.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Two modes are available:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Non-conversational mode&lt;/strong&gt;: Every step is processed independently.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conversational mode&lt;/strong&gt;: The agent maintains memory across multiple interactions, reducing the need to repeat context.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5 id=&#34;example-task&#34;&gt;Example Task:&lt;/h5&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;web_agent_conversation(&amp;#34;gemini-2.0-flash&amp;#34;, &amp;#34;Search for &amp;#39;LLM agents&amp;#39; and return the first result&amp;#39;s title.&amp;#34;, &amp;#34;https://duckduckgo.com/&amp;#34;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This runs a search query and extracts the first result’s title, all without predefined scraping rules.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-did-it-go&#34;&gt;How Did It Go&lt;/h4&gt;&#xA;&lt;p&gt;At one point, the agent was not using valid CSS selector syntax and couldn&amp;rsquo;t &amp;ldquo;click&amp;rdquo; the search button. In spite of not getting to the search results page, the agent returned a &amp;ldquo;successful&amp;rdquo; answer. I wondered if the LLM was somehow using its trained knowledge to find a valid answer, but I could not find the result anywhere. I searched DuckDuckGo and Google for the title.&lt;/p&gt;&#xA;&lt;p&gt;I added the &amp;ldquo;Explain how you solved the task&amp;rdquo; prompt and the agent replied that since it was not able to get to the search results, it created a &lt;strong&gt;hypothetical answer&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I did two things:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I told the agent it was not allowed to make up answers. Just fail gracefully.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I gave the agent examples of valid CSS selectors for &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, and &lt;code&gt;attribute&lt;/code&gt; selectors. This really improved the CSS selector accuracy. I had hoped the LLM&amp;rsquo;s training would have been good enough.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The &lt;strong&gt;conversational&lt;/strong&gt; mode, unsurprisingly, could finish tasks with fewer steps. Memory and retained context matter.&lt;/p&gt;&#xA;&lt;h3 id=&#34;tool-agent-using-llms-to-call-functions&#34;&gt;Tool Agent: Using LLMs to Call Functions&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;strong&gt;Tool Agent&lt;/strong&gt; extends an LLM’s capabilities by allowing it to call external functions. Instead of just answering questions, it can interact with a set of predefined tools—simulating API calls, performing calculations, retrieving weather data, and more.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-it-works-1&#34;&gt;How It Works:&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;registry of tool functions&lt;/strong&gt; provides capabilities like:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Web search (&lt;code&gt;search_web&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Weather lookup (&lt;code&gt;get_weather&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Date and time retrieval (&lt;code&gt;get_datetime&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The agent follows a &lt;strong&gt;conversational loop&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Receives a user query.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Decides whether a tool is needed.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Calls the tool and processes the response.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Outputs the final answer.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5 id=&#34;example-interaction&#34;&gt;Example Interaction:&lt;/h5&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: What&amp;#39;s the weather in Beverly Hills?&#xA;Function result: {&amp;#39;zipcode&amp;#39;: &amp;#39;90210&amp;#39;}&#xA;Function result: {&amp;#39;temperature&amp;#39;: &amp;#39;75 F&amp;#39;, &amp;#39;conditions&amp;#39;: &amp;#39;Sunny&amp;#39;}&#xA;Agent: The weather in Beverly Hills (zipcode 90210) is 75 F and Sunny.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, the LLM &lt;strong&gt;autonomously&lt;/strong&gt; determines that it needs to retrieve a zip code first before getting the weather.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-did-it-go-1&#34;&gt;How Did It Go&lt;/h4&gt;&#xA;&lt;p&gt;It&amp;rsquo;s not easy to get an LLM to only and always respond using structured output, such as JSON. Some models do better than others, and there are lots of ways to use the system prompt to help get the results you want. I found that I still need to check for Markdown code fences in the output, and remove those.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I saw Simon Willison just &lt;a href=&#34;https://simonwillison.net/2025/Feb/28/llm-schemas/&#34;&gt;updated&lt;/a&gt; &lt;code&gt;llm&lt;/code&gt; to support &lt;em&gt;schemas&lt;/em&gt;, to make structured output easier.&lt;/p&gt;&#xA;&lt;p&gt;Getting the agent to use the tools (Python functions) required not only being specific about the JSON format and the parameters, but also showing examples. The examples seemed to help a lot. I found some discussions about using XML formatted block to describe the set of tools in the system prompt. Something about LLMs being able to handle XML better than JSON. Maybe that is outdated?&lt;/p&gt;&#xA;&lt;p&gt;I was pretty happy to see the agent use two successive tools (as shown above) to complete a task. I want to play around more to see how that type of chaining can be improved and expanded.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What’s Next?&lt;/h2&gt;&#xA;&lt;p&gt;This has been a fun project and I think there are a few more things I want to try before moving on to the real frameworks:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Expanding the set of tools to include real API integrations.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use separate agents to implement tools.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Fine-tuning the prompt engineering for better decision-making.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Improving the agent’s ability to recover from errors.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://simonwillison.net/&#34;&gt;Simon Willison&amp;rsquo;s blog&lt;/a&gt; is a great place to learn about LLMs and keep updated&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/browser-use/browser-use&#34;&gt;browser-use&lt;/a&gt; is a full featured Python framework for creating Browsing using research agents&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ai.pydantic.dev/&#34;&gt;PydanticAI&lt;/a&gt; is a full featured Python library that makes it easy to get started building tool-using agents&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Large Language Models (LLMs) are powerful tools for generating text, answering questions, and coding. We&amp;rsquo;ve moved beyond generating content, and LLMs are now being used to take actions as &lt;strong&gt;agents&lt;/strong&gt; — independent entities that can act, use tools, and interact with their environment. You probably already know all of this.&lt;/p&gt;&#xA;&lt;p&gt;I wanted to explore using LLMs as agents, but I like to get an understanding of the underlying components before using high-level frameworks to hide all of the minutiae and make the process of building production-ready systems. Understanding how the different components work and interact is important to my own learning process.&lt;/p&gt;&#xA;&lt;p&gt;That’s exactly what my &lt;a href=&#34;https://github.com/mfinkle/llm-agents&#34;&gt;&lt;strong&gt;LLM Agents&lt;/strong&gt; project&lt;/a&gt; sets out to do. Instead of relying on frameworks that abstract away the details, the project takes a &lt;strong&gt;bare-bones approach&lt;/strong&gt; to learning how LLMs can function as agents. By minimizing dependencies, I can get a clearer understanding of the challenges, possibilities, and mechanics of building LLM-powered agents.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-minimal-dependencies-matter-to-me&#34;&gt;Why Minimal Dependencies Matter (To Me)&lt;/h2&gt;&#xA;&lt;p&gt;Many existing frameworks promise powerful LLM agent capabilities, but they often come at the cost of &lt;strong&gt;hiding the underlying complexities&lt;/strong&gt;. While these frameworks can be useful, starting with minimal dependencies allows us to:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Understand the fundamentals&lt;/strong&gt;: How does an LLM process information to take actions? How does the system prompt impact the effectiveness of the agent?&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Explore limitations&lt;/strong&gt;: What challenges arise when an agent tries to perform a multi-step task? How does the shape of the tools (functions or APIs) impact how the agent can process the flow.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Control the design&lt;/strong&gt;: Without being boxed into a framework’s way of doing things, we can experiment freely. We can then use this knowledge to help pick the right type of framework for more advanced and production use cases.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This project keeps things simple, using only a lightweight LLM library, Simon Willison&amp;rsquo;s great &lt;code&gt;llm&lt;/code&gt; Python library (&lt;a href=&#34;https://github.com/simonw/llm&#34;&gt;code&lt;/a&gt; &amp;amp; &lt;a href=&#34;https://llm.datasette.io/en/stable/&#34;&gt;docs&lt;/a&gt;), and &lt;a href=&#34;https://playwright.dev&#34;&gt;Playwright&lt;/a&gt; for handling web automation.&lt;/p&gt;&#xA;&lt;p&gt;These agents are not production ready, but they are trimmed down enough to see the mechanisms at work.&lt;/p&gt;&#xA;&lt;h2 id=&#34;meet-the-agents&#34;&gt;Meet the Agents&lt;/h2&gt;&#xA;&lt;p&gt;The repository contains &lt;strong&gt;two primary agents&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;h3 id=&#34;web-agent-navigating-the-web-with-llms&#34;&gt;Web Agent: Navigating the Web with LLMs&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;strong&gt;Web Agent&lt;/strong&gt; is designed to interact with websites using the &lt;a href=&#34;https://playwright.dev/python/docs/intro&#34;&gt;Playwright&lt;/a&gt; Python library. Instead of treating a webpage as structured data, this agent lets an LLM interpret raw HTML and decide what actions to take—whether that means clicking a button, typing into a form, or extracting text. I wanted to see how well an agent could navigate something as confusing as a modern website. If you&amp;rsquo;ve ever done a &amp;ldquo;view source&amp;rdquo; or &amp;ldquo;inspect&amp;rdquo; on a modern webpage, you know what I mean.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;PageManager&lt;/strong&gt; class handles the browser automation using Playwright.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The &lt;strong&gt;LLM generates the next action&lt;/strong&gt; based on the current page content and the assigned task.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Two modes are available:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Non-conversational mode&lt;/strong&gt;: Every step is processed independently.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;strong&gt;Conversational mode&lt;/strong&gt;: The agent maintains memory across multiple interactions, reducing the need to repeat context.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5 id=&#34;example-task&#34;&gt;Example Task:&lt;/h5&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;web_agent_conversation(&amp;#34;gemini-2.0-flash&amp;#34;, &amp;#34;Search for &amp;#39;LLM agents&amp;#39; and return the first result&amp;#39;s title.&amp;#34;, &amp;#34;https://duckduckgo.com/&amp;#34;)&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This runs a search query and extracts the first result’s title, all without predefined scraping rules.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-did-it-go&#34;&gt;How Did It Go&lt;/h4&gt;&#xA;&lt;p&gt;At one point, the agent was not using valid CSS selector syntax and couldn&amp;rsquo;t &amp;ldquo;click&amp;rdquo; the search button. In spite of not getting to the search results page, the agent returned a &amp;ldquo;successful&amp;rdquo; answer. I wondered if the LLM was somehow using its trained knowledge to find a valid answer, but I could not find the result anywhere. I searched DuckDuckGo and Google for the title.&lt;/p&gt;&#xA;&lt;p&gt;I added the &amp;ldquo;Explain how you solved the task&amp;rdquo; prompt and the agent replied that since it was not able to get to the search results, it created a &lt;strong&gt;hypothetical answer&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I did two things:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I told the agent it was not allowed to make up answers. Just fail gracefully.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;I gave the agent examples of valid CSS selectors for &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, and &lt;code&gt;attribute&lt;/code&gt; selectors. This really improved the CSS selector accuracy. I had hoped the LLM&amp;rsquo;s training would have been good enough.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The &lt;strong&gt;conversational&lt;/strong&gt; mode, unsurprisingly, could finish tasks with fewer steps. Memory and retained context matter.&lt;/p&gt;&#xA;&lt;h3 id=&#34;tool-agent-using-llms-to-call-functions&#34;&gt;Tool Agent: Using LLMs to Call Functions&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;strong&gt;Tool Agent&lt;/strong&gt; extends an LLM’s capabilities by allowing it to call external functions. Instead of just answering questions, it can interact with a set of predefined tools—simulating API calls, performing calculations, retrieving weather data, and more.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-it-works-1&#34;&gt;How It Works:&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;A &lt;strong&gt;registry of tool functions&lt;/strong&gt; provides capabilities like:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Web search (&lt;code&gt;search_web&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Weather lookup (&lt;code&gt;get_weather&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Date and time retrieval (&lt;code&gt;get_datetime&lt;/code&gt;)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;The agent follows a &lt;strong&gt;conversational loop&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Receives a user query.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Decides whether a tool is needed.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Calls the tool and processes the response.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Outputs the final answer.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h5 id=&#34;example-interaction&#34;&gt;Example Interaction:&lt;/h5&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;You: What&amp;#39;s the weather in Beverly Hills?&#xA;Function result: {&amp;#39;zipcode&amp;#39;: &amp;#39;90210&amp;#39;}&#xA;Function result: {&amp;#39;temperature&amp;#39;: &amp;#39;75 F&amp;#39;, &amp;#39;conditions&amp;#39;: &amp;#39;Sunny&amp;#39;}&#xA;Agent: The weather in Beverly Hills (zipcode 90210) is 75 F and Sunny.&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here, the LLM &lt;strong&gt;autonomously&lt;/strong&gt; determines that it needs to retrieve a zip code first before getting the weather.&lt;/p&gt;&#xA;&lt;h4 id=&#34;how-did-it-go-1&#34;&gt;How Did It Go&lt;/h4&gt;&#xA;&lt;p&gt;It&amp;rsquo;s not easy to get an LLM to only and always respond using structured output, such as JSON. Some models do better than others, and there are lots of ways to use the system prompt to help get the results you want. I found that I still need to check for Markdown code fences in the output, and remove those.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I saw Simon Willison just &lt;a href=&#34;https://simonwillison.net/2025/Feb/28/llm-schemas/&#34;&gt;updated&lt;/a&gt; &lt;code&gt;llm&lt;/code&gt; to support &lt;em&gt;schemas&lt;/em&gt;, to make structured output easier.&lt;/p&gt;&#xA;&lt;p&gt;Getting the agent to use the tools (Python functions) required not only being specific about the JSON format and the parameters, but also showing examples. The examples seemed to help a lot. I found some discussions about using XML formatted block to describe the set of tools in the system prompt. Something about LLMs being able to handle XML better than JSON. Maybe that is outdated?&lt;/p&gt;&#xA;&lt;p&gt;I was pretty happy to see the agent use two successive tools (as shown above) to complete a task. I want to play around more to see how that type of chaining can be improved and expanded.&lt;/p&gt;&#xA;&lt;h2 id=&#34;whats-next&#34;&gt;What’s Next?&lt;/h2&gt;&#xA;&lt;p&gt;This has been a fun project and I think there are a few more things I want to try before moving on to the real frameworks:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Expanding the set of tools to include real API integrations.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Use separate agents to implement tools.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Fine-tuning the prompt engineering for better decision-making.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Improving the agent’s ability to recover from errors.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;further-reading&#34;&gt;Further Reading&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://simonwillison.net/&#34;&gt;Simon Willison&amp;rsquo;s blog&lt;/a&gt; is a great place to learn about LLMs and keep updated&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/browser-use/browser-use&#34;&gt;browser-use&lt;/a&gt; is a full featured Python framework for creating Browsing using research agents&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://ai.pydantic.dev/&#34;&gt;PydanticAI&lt;/a&gt; is a full featured Python library that makes it easy to get started building tool-using agents&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Work-as-Imagined vs Work-as-Done</title>
				<link>https://starkravingfinkle.org/posts/2024/04/work-as-imagined-vs-work-as-done/</link>
				<pubDate>Mon, 15 Apr 2024 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2024/04/work-as-imagined-vs-work-as-done/</guid>
				<description>&lt;p&gt;With engineering focus on reducing incidents and improving operational reliability, I frequently come back to the realization that &lt;strong&gt;humans are fallible&lt;/strong&gt; and we should be learning ways to &lt;strong&gt;nudge people toward success rather than failure&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There are whole industries and research machines built around the study of Human Factors, and how to improve safety, reliability, and quality. One topic that struck me as extremely useful to software engineering was the concepts of Work-as-Imagined (WAI) versus Work-as-Done (WAD). Anytime you’ve heard “&lt;em&gt;the system failed because someone executed a process differently than it was documented&lt;/em&gt;” could be a WAI vs WAD issue. This comes up a lot in healthcare, manufacturing, and transportation — where accidents can have horrible consequences.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Full disclosure: There are actually many varieties of human work, but WAI and WAD are good enough to make the point. Steven Shorrock covers the subject so well on his blog: Humanistic Systems &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;work-as-imagined&#34;&gt;Work-as-Imagined&lt;/h1&gt;&#xA;&lt;p&gt;When thinking about a process or set of tasks that make up work, we need to imagine the steps and work others must do to accomplish the tasks. We do this for many good reasons, like scheduling, planning, and forecasting. WAI is usually formed by past experiences of actually doing work. While this is a good starting point, it’s likely the situation, assumptions, and variables are not the same.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;To a greater or lesser extent, all of these imaginations – or mental models – will be wrong; our imagination of others’ work is a gross simplification, is incomplete, and is also fundamentally incorrect in various ways, depending partly on the differences in work and context between the imaginer and the imagined. — &lt;a href=&#34;https://humanisticsystems.com/2016/12/05/the-varieties-of-human-work/&#34;&gt;The Varieties of Human Work&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h1 id=&#34;work-as-done&#34;&gt;Work-as-Done&lt;/h1&gt;&#xA;&lt;p&gt;Work-as-Done is literally the work people do. It happens in the real world, under a variety of different conditions and variables.  It’s hard to document WAD because of the unique situation in which the work was done and the specific adjustments and tradeoffs required to complete the work for a given situation.&lt;/p&gt;&#xA;&lt;p&gt;In any normal day, a work-as-done day, people:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Adapt and adjust to situations and change their actions accordingly&lt;/li&gt;&#xA;&lt;li&gt;Deal with unintended consequences and unexpected situations&lt;/li&gt;&#xA;&lt;li&gt;Interpret policies and procedures and apply them to match the conditions&lt;/li&gt;&#xA;&lt;li&gt;Detect and correct when something is about to go wrong and intervene to prevent it from happening&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 id=&#34;mind-the-gap&#34;&gt;Mind the Gap&lt;/h1&gt;&#xA;&lt;p&gt;Monitoring the gap between the WAI and the WAD of a given task has been highlighted as an important practice for organizations aiming to achieve high reliability. The gap between WAI and WAD can result in “human error” conditions. We frequently hear about incidents and accidents that were caused by “human error” in a variety of situations:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Air traffic near misses at airports&lt;/li&gt;&#xA;&lt;li&gt;Train derailments and accidents&lt;/li&gt;&#xA;&lt;li&gt;Critical computer systems taken offline&lt;/li&gt;&#xA;&lt;li&gt;Mistakes made during medical procedures&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It’s natural for us to blame the problem on the gap — people didn’t follow the process — and try to improve reliability and reduce errors by focusing on stricter adherence to WAI. Perhaps unsurprisingly, &lt;strong&gt;this results in more rules and processes which can certainly slow down overall productivity&lt;/strong&gt;, and even increase the gap between WAI and WAD.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Safety management must correspond to Work-As-Done and not rely on Work-As-Imagined. — &lt;a href=&#34;https://www.skybrary.aero/sites/default/files/bookshelf/3934.pdf&#34;&gt;Can We Ever Imagine How Work is Done?&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;In recent decades, there is more focus on WAD. Examining the reasons why the WAD gap exists and working to align WAI more closely with WAD. Embracing the reality of how the work is done and working to formalize it. Instead of optimizing for the way we imagine work is done, we acknowledge the way work is actually done.&lt;/p&gt;&#xA;&lt;h1 id=&#34;closing-the-gap&#34;&gt;Closing the Gap&lt;/h1&gt;&#xA;&lt;p&gt;In my work, production incidents that occur in software systems are an easy area to find WAI vs WAD happening. Incident management and postmortems have best practices that usually involve blameless reviews of what led to the incident. In many case, the easiest answer to &amp;ldquo;&lt;em&gt;&lt;a href=&#34;https://surfingcomplexity.blog/2021/11/22/what-could-we-have-done-differently/&#34;&gt;how can we stop this incident from happening again?&lt;/a&gt;&lt;/em&gt;&amp;rdquo; is better documentation and more process.&lt;/p&gt;&#xA;&lt;p&gt;Modern incident management is focusing more on &lt;a href=&#34;https://www.learningfromincidents.io/&#34;&gt;learning from incidents&lt;/a&gt; and less about root-cause analysis. One reason is that incidents &lt;strong&gt;rarely&lt;/strong&gt; happen the exact same way in the future. Focusing on fixing a specific incident yields less value than learning about how the system worked to create the incident in the first place. Learning about &lt;a href=&#34;https://surfingcomplexity.blog/2023/08/20/why-lfi-is-a-tough-sell/&#34;&gt;how your system work in production is harder&lt;/a&gt;, but yields more impact in discovering weak parts of the systems.&lt;/p&gt;&#xA;&lt;p&gt;This section could be an entire book, or at least several posts, so I&amp;rsquo;ll leave it to you to read some of the links.&lt;/p&gt;&#xA;&lt;h1 id=&#34;desire-paths&#34;&gt;Desire Paths&lt;/h1&gt;&#xA;&lt;p&gt;The whole WAI vs WAD discussion reminds me of &lt;a href=&#34;https://en.wikipedia.org/wiki/Desire_path&#34;&gt;desire paths&lt;/a&gt;, which visually show the difference between the planned and actual outcomes.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Desire paths typically emerge as convenient shortcuts where more deliberately constructed paths take a longer or more circuitous route, have gaps, or are non-existent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/image-2.png&#34; alt=&#34;&#34;&gt;&#xA;&lt;img src=&#34;images/image-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/image.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Tying together desire paths with WAI &amp;amp; WAD, some &lt;a href=&#34;https://99percentinvisible.org/article/least-resistance-desire-paths-can-lead-better-design/&#34;&gt;universities and cities have reportedly&lt;/a&gt; &lt;em&gt;waited to see which routes people would take regularly&lt;/em&gt; before deciding where to pave additional pathways across their campuses and walking paths.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;With engineering focus on reducing incidents and improving operational reliability, I frequently come back to the realization that &lt;strong&gt;humans are fallible&lt;/strong&gt; and we should be learning ways to &lt;strong&gt;nudge people toward success rather than failure&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;There are whole industries and research machines built around the study of Human Factors, and how to improve safety, reliability, and quality. One topic that struck me as extremely useful to software engineering was the concepts of Work-as-Imagined (WAI) versus Work-as-Done (WAD). Anytime you’ve heard “&lt;em&gt;the system failed because someone executed a process differently than it was documented&lt;/em&gt;” could be a WAI vs WAD issue. This comes up a lot in healthcare, manufacturing, and transportation — where accidents can have horrible consequences.&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Full disclosure: There are actually many varieties of human work, but WAI and WAD are good enough to make the point. Steven Shorrock covers the subject so well on his blog: Humanistic Systems &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;work-as-imagined&#34;&gt;Work-as-Imagined&lt;/h1&gt;&#xA;&lt;p&gt;When thinking about a process or set of tasks that make up work, we need to imagine the steps and work others must do to accomplish the tasks. We do this for many good reasons, like scheduling, planning, and forecasting. WAI is usually formed by past experiences of actually doing work. While this is a good starting point, it’s likely the situation, assumptions, and variables are not the same.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;To a greater or lesser extent, all of these imaginations – or mental models – will be wrong; our imagination of others’ work is a gross simplification, is incomplete, and is also fundamentally incorrect in various ways, depending partly on the differences in work and context between the imaginer and the imagined. — &lt;a href=&#34;https://humanisticsystems.com/2016/12/05/the-varieties-of-human-work/&#34;&gt;The Varieties of Human Work&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h1 id=&#34;work-as-done&#34;&gt;Work-as-Done&lt;/h1&gt;&#xA;&lt;p&gt;Work-as-Done is literally the work people do. It happens in the real world, under a variety of different conditions and variables.  It’s hard to document WAD because of the unique situation in which the work was done and the specific adjustments and tradeoffs required to complete the work for a given situation.&lt;/p&gt;&#xA;&lt;p&gt;In any normal day, a work-as-done day, people:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Adapt and adjust to situations and change their actions accordingly&lt;/li&gt;&#xA;&lt;li&gt;Deal with unintended consequences and unexpected situations&lt;/li&gt;&#xA;&lt;li&gt;Interpret policies and procedures and apply them to match the conditions&lt;/li&gt;&#xA;&lt;li&gt;Detect and correct when something is about to go wrong and intervene to prevent it from happening&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h1 id=&#34;mind-the-gap&#34;&gt;Mind the Gap&lt;/h1&gt;&#xA;&lt;p&gt;Monitoring the gap between the WAI and the WAD of a given task has been highlighted as an important practice for organizations aiming to achieve high reliability. The gap between WAI and WAD can result in “human error” conditions. We frequently hear about incidents and accidents that were caused by “human error” in a variety of situations:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Air traffic near misses at airports&lt;/li&gt;&#xA;&lt;li&gt;Train derailments and accidents&lt;/li&gt;&#xA;&lt;li&gt;Critical computer systems taken offline&lt;/li&gt;&#xA;&lt;li&gt;Mistakes made during medical procedures&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It’s natural for us to blame the problem on the gap — people didn’t follow the process — and try to improve reliability and reduce errors by focusing on stricter adherence to WAI. Perhaps unsurprisingly, &lt;strong&gt;this results in more rules and processes which can certainly slow down overall productivity&lt;/strong&gt;, and even increase the gap between WAI and WAD.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Safety management must correspond to Work-As-Done and not rely on Work-As-Imagined. — &lt;a href=&#34;https://www.skybrary.aero/sites/default/files/bookshelf/3934.pdf&#34;&gt;Can We Ever Imagine How Work is Done?&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;In recent decades, there is more focus on WAD. Examining the reasons why the WAD gap exists and working to align WAI more closely with WAD. Embracing the reality of how the work is done and working to formalize it. Instead of optimizing for the way we imagine work is done, we acknowledge the way work is actually done.&lt;/p&gt;&#xA;&lt;h1 id=&#34;closing-the-gap&#34;&gt;Closing the Gap&lt;/h1&gt;&#xA;&lt;p&gt;In my work, production incidents that occur in software systems are an easy area to find WAI vs WAD happening. Incident management and postmortems have best practices that usually involve blameless reviews of what led to the incident. In many case, the easiest answer to &amp;ldquo;&lt;em&gt;&lt;a href=&#34;https://surfingcomplexity.blog/2021/11/22/what-could-we-have-done-differently/&#34;&gt;how can we stop this incident from happening again?&lt;/a&gt;&lt;/em&gt;&amp;rdquo; is better documentation and more process.&lt;/p&gt;&#xA;&lt;p&gt;Modern incident management is focusing more on &lt;a href=&#34;https://www.learningfromincidents.io/&#34;&gt;learning from incidents&lt;/a&gt; and less about root-cause analysis. One reason is that incidents &lt;strong&gt;rarely&lt;/strong&gt; happen the exact same way in the future. Focusing on fixing a specific incident yields less value than learning about how the system worked to create the incident in the first place. Learning about &lt;a href=&#34;https://surfingcomplexity.blog/2023/08/20/why-lfi-is-a-tough-sell/&#34;&gt;how your system work in production is harder&lt;/a&gt;, but yields more impact in discovering weak parts of the systems.&lt;/p&gt;&#xA;&lt;p&gt;This section could be an entire book, or at least several posts, so I&amp;rsquo;ll leave it to you to read some of the links.&lt;/p&gt;&#xA;&lt;h1 id=&#34;desire-paths&#34;&gt;Desire Paths&lt;/h1&gt;&#xA;&lt;p&gt;The whole WAI vs WAD discussion reminds me of &lt;a href=&#34;https://en.wikipedia.org/wiki/Desire_path&#34;&gt;desire paths&lt;/a&gt;, which visually show the difference between the planned and actual outcomes.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Desire paths typically emerge as convenient shortcuts where more deliberately constructed paths take a longer or more circuitous route, have gaps, or are non-existent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/image-2.png&#34; alt=&#34;&#34;&gt;&#xA;&lt;img src=&#34;images/image-1.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/image.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Tying together desire paths with WAI &amp;amp; WAD, some &lt;a href=&#34;https://99percentinvisible.org/article/least-resistance-desire-paths-can-lead-better-design/&#34;&gt;universities and cities have reportedly&lt;/a&gt; &lt;em&gt;waited to see which routes people would take regularly&lt;/em&gt; before deciding where to pave additional pathways across their campuses and walking paths.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Information Flows in Organizations</title>
				<link>https://starkravingfinkle.org/posts/2024/04/information-flows-in-organizations/</link>
				<pubDate>Wed, 10 Apr 2024 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2024/04/information-flows-in-organizations/</guid>
				<description>&lt;p&gt;I&amp;rsquo;ve had cause to looked into research and ideas about the ways information flows within organizations. Discussions about transparency, decision making, empowering teams, and trust seem to intersect at organizational communication and information flows.&lt;/p&gt;&#xA;&lt;p&gt;One of my favorite people to follow in this space is &lt;a href=&#34;https://itrevolution.com/devops-blog/&#34;&gt;Gene Kim&lt;/a&gt; (&lt;a href=&#34;https://itrevolution.com/book/the-phoenix-project/&#34;&gt;Phoenix Project&lt;/a&gt;, &lt;a href=&#34;https://itrevolution.com/book/the-devops-handbook/&#34;&gt;DevOps Handbook&lt;/a&gt;, &lt;a href=&#34;https://itrevolution.com/book/accelerate/&#34;&gt;Accelerate&lt;/a&gt;, and &lt;a href=&#34;https://www.devops-research.com/research.html&#34;&gt;DORA Reports&lt;/a&gt;). He has done a few &lt;a href=&#34;https://itrevolution.com/the-idealcast-podcast/&#34;&gt;podcasts&lt;/a&gt; that focused on relevant topics and concluded that &lt;strong&gt;you can predict whether an organization is a high performer or a low performer&lt;/strong&gt;, just by looking at the communication paths of an organization, as well as their frequency and intensity. (&lt;a href=&#34;https://itrevolution.com/podcast/the-idealcast-episode-16/&#34;&gt;Episode 16&lt;/a&gt;, @54 min)&lt;/p&gt;&#xA;&lt;p&gt;Some of these ideas might resonate with you. There are generally two forms of information flows:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Slow flows&lt;/strong&gt; where we need detailed granularity and accuracy of information. Leadership usually needs to be involved in these discussions so communication tends to escalate up and down hierarchies.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Fast flows&lt;/strong&gt; where frequency and speed tend to be more important. These flows occur in the operational realm, where work is executed, and happen directly between teams using existing interfaces.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In the ideal case, a majority of the communication is happening within and between teams using fast flows. Forcing escalation up and down the hierarchy means getting people involved who probably don&amp;rsquo;t have a solid grasp of the details. Interactions are slow and likely lead to poor decisions. On the other hand, when teammates talk to each other or where there are sanctioned ways for teams to work with each other with a shared goal, integrated problem solving is very fast.&lt;/p&gt;&#xA;&lt;p&gt;This doesn&amp;rsquo;t mean all information flows should be fast. There are &lt;strong&gt;two phases where slow flows are critical&lt;/strong&gt;: Upfront planning and Retrospective assessment. Planning and preparation are the activities where we need leaders to be thoughtful about defining the goals and then defining responsibilities and the structures to support them. Later, slow communications come back when we assess and improve our performance and outcomes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;thinking-fast-and-slow&#34;&gt;Thinking, Fast and Slow&lt;/h2&gt;&#xA;&lt;p&gt;I want to be clear that fast and slow information flows are different concepts than the fast and slow modes of thinking explored in Daniel Kahneman&amp;rsquo;s book &lt;em&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow&#34;&gt;Thinking, Fast and Slow&lt;/a&gt;&lt;/em&gt;. The book explores two systems of thinking that drive the way humans make decisions.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;System 1 (Fast Thinking):&lt;/strong&gt; This system is intuitive, automatic, and operates quickly with little effort or voluntary control. It&amp;rsquo;s responsible for quick decisions, habits, and reactions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;System 2 (Slow Thinking):&lt;/strong&gt; This system is deliberate, analytical, and requires effortful mental activity. It&amp;rsquo;s used for complex computations, learning new information, and solving difficult problems.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Kahneman discusses how these two systems can work together, but sometimes lead to biases and errors in judgment. He talks about how these modes can affect decision-making and offers suggestions into how we can become more aware of these biases to make better decisions.&lt;/p&gt;&#xA;&lt;p&gt;Obviously another area worth exploring to help understand how organizations can support people to create better outcomes.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I&amp;rsquo;ve had cause to looked into research and ideas about the ways information flows within organizations. Discussions about transparency, decision making, empowering teams, and trust seem to intersect at organizational communication and information flows.&lt;/p&gt;&#xA;&lt;p&gt;One of my favorite people to follow in this space is &lt;a href=&#34;https://itrevolution.com/devops-blog/&#34;&gt;Gene Kim&lt;/a&gt; (&lt;a href=&#34;https://itrevolution.com/book/the-phoenix-project/&#34;&gt;Phoenix Project&lt;/a&gt;, &lt;a href=&#34;https://itrevolution.com/book/the-devops-handbook/&#34;&gt;DevOps Handbook&lt;/a&gt;, &lt;a href=&#34;https://itrevolution.com/book/accelerate/&#34;&gt;Accelerate&lt;/a&gt;, and &lt;a href=&#34;https://www.devops-research.com/research.html&#34;&gt;DORA Reports&lt;/a&gt;). He has done a few &lt;a href=&#34;https://itrevolution.com/the-idealcast-podcast/&#34;&gt;podcasts&lt;/a&gt; that focused on relevant topics and concluded that &lt;strong&gt;you can predict whether an organization is a high performer or a low performer&lt;/strong&gt;, just by looking at the communication paths of an organization, as well as their frequency and intensity. (&lt;a href=&#34;https://itrevolution.com/podcast/the-idealcast-episode-16/&#34;&gt;Episode 16&lt;/a&gt;, @54 min)&lt;/p&gt;&#xA;&lt;p&gt;Some of these ideas might resonate with you. There are generally two forms of information flows:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Slow flows&lt;/strong&gt; where we need detailed granularity and accuracy of information. Leadership usually needs to be involved in these discussions so communication tends to escalate up and down hierarchies.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Fast flows&lt;/strong&gt; where frequency and speed tend to be more important. These flows occur in the operational realm, where work is executed, and happen directly between teams using existing interfaces.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In the ideal case, a majority of the communication is happening within and between teams using fast flows. Forcing escalation up and down the hierarchy means getting people involved who probably don&amp;rsquo;t have a solid grasp of the details. Interactions are slow and likely lead to poor decisions. On the other hand, when teammates talk to each other or where there are sanctioned ways for teams to work with each other with a shared goal, integrated problem solving is very fast.&lt;/p&gt;&#xA;&lt;p&gt;This doesn&amp;rsquo;t mean all information flows should be fast. There are &lt;strong&gt;two phases where slow flows are critical&lt;/strong&gt;: Upfront planning and Retrospective assessment. Planning and preparation are the activities where we need leaders to be thoughtful about defining the goals and then defining responsibilities and the structures to support them. Later, slow communications come back when we assess and improve our performance and outcomes.&lt;/p&gt;&#xA;&lt;h2 id=&#34;thinking-fast-and-slow&#34;&gt;Thinking, Fast and Slow&lt;/h2&gt;&#xA;&lt;p&gt;I want to be clear that fast and slow information flows are different concepts than the fast and slow modes of thinking explored in Daniel Kahneman&amp;rsquo;s book &lt;em&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow&#34;&gt;Thinking, Fast and Slow&lt;/a&gt;&lt;/em&gt;. The book explores two systems of thinking that drive the way humans make decisions.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;System 1 (Fast Thinking):&lt;/strong&gt; This system is intuitive, automatic, and operates quickly with little effort or voluntary control. It&amp;rsquo;s responsible for quick decisions, habits, and reactions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;System 2 (Slow Thinking):&lt;/strong&gt; This system is deliberate, analytical, and requires effortful mental activity. It&amp;rsquo;s used for complex computations, learning new information, and solving difficult problems.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Kahneman discusses how these two systems can work together, but sometimes lead to biases and errors in judgment. He talks about how these modes can affect decision-making and offers suggestions into how we can become more aware of these biases to make better decisions.&lt;/p&gt;&#xA;&lt;p&gt;Obviously another area worth exploring to help understand how organizations can support people to create better outcomes.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Project: Networked LED Pixel Display</title>
				<link>https://starkravingfinkle.org/posts/2022/04/project-networked-led-pixel-display/</link>
				<pubDate>Fri, 08 Apr 2022 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2022/04/project-networked-led-pixel-display/</guid>
				<description>&lt;p&gt;I have been wanting to play around with an ESP32-based micro for a while. Once I became comfortable with Adafruit&amp;rsquo;s microcontrollers and &lt;a href=&#34;https://circuitpython.org/&#34;&gt;CircuitPython&lt;/a&gt;, I thought I&amp;rsquo;d try out some of their ESP32 offerings. I bought a few &lt;a href=&#34;https://www.adafruit.com/product/4264&#34;&gt;Airlift (ESP32) Featherwings&lt;/a&gt; to use with the &lt;a href=&#34;https://www.adafruit.com/feather2040&#34;&gt;Feather RP2040&lt;/a&gt; boards I was experimenting with.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve also been messing around with some WS2818 / NeoPixel LED 8×8 and 16x16 grids, so I thought it might be interesting to work on a web-based pixel display.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-raw-circuit.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;CircuitPython has some very handy libraries for building &lt;a href=&#34;https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation&#34;&gt;LED animations&lt;/a&gt; on strings or grids of WS / Neopixels. The Airlift ESP32 also has a library to create network clients and access points—surely this wouldn&amp;rsquo;t be too hard.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the shapes I used 3D print the parts:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-parts-1024x437.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;(I really need to improve my enclosure design skills)&lt;/p&gt;&#xA;&lt;p&gt;Something I&amp;rsquo;ve picked up from other people making an LED grid displays: Use a lattice grid and diffuser to create an even &amp;ldquo;pixel&amp;rdquo; instead of a bright point of light (depending on your tastes). It took a few tries to get the lattice to match nicely with the LED matrix circuit board. While most people use an acrylic diffuser, I just used a piece of card stock paper.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-grid.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-grid-diffuser-test.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Using the CircuitPython LED animations library, it was relatively easy to try out a variety of different LED matrix animation patterns. Building on the primitives in the library, I created additional functionality that supports &lt;a href=&#34;https://starkravingfinkle.org/posts/2021/12/circuitpython-leds-and-animations/&#34;&gt;text-based and sprite-based animations&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/animation_extras.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-complete-748x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;With so many different patterns, sprites, and options to manage—I decided to use a web-based UI to handle the experience.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-webui-display-473x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-webui-settings-473x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The UI is served from the device itself. This turned out to be more challenging than you might think. I&amp;rsquo;ll do a separate post on serving a web-based UI while also running LED animations on the device.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I have been wanting to play around with an ESP32-based micro for a while. Once I became comfortable with Adafruit&amp;rsquo;s microcontrollers and &lt;a href=&#34;https://circuitpython.org/&#34;&gt;CircuitPython&lt;/a&gt;, I thought I&amp;rsquo;d try out some of their ESP32 offerings. I bought a few &lt;a href=&#34;https://www.adafruit.com/product/4264&#34;&gt;Airlift (ESP32) Featherwings&lt;/a&gt; to use with the &lt;a href=&#34;https://www.adafruit.com/feather2040&#34;&gt;Feather RP2040&lt;/a&gt; boards I was experimenting with.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve also been messing around with some WS2818 / NeoPixel LED 8×8 and 16x16 grids, so I thought it might be interesting to work on a web-based pixel display.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-raw-circuit.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;CircuitPython has some very handy libraries for building &lt;a href=&#34;https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation&#34;&gt;LED animations&lt;/a&gt; on strings or grids of WS / Neopixels. The Airlift ESP32 also has a library to create network clients and access points—surely this wouldn&amp;rsquo;t be too hard.&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s the shapes I used 3D print the parts:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-parts-1024x437.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;(I really need to improve my enclosure design skills)&lt;/p&gt;&#xA;&lt;p&gt;Something I&amp;rsquo;ve picked up from other people making an LED grid displays: Use a lattice grid and diffuser to create an even &amp;ldquo;pixel&amp;rdquo; instead of a bright point of light (depending on your tastes). It took a few tries to get the lattice to match nicely with the LED matrix circuit board. While most people use an acrylic diffuser, I just used a piece of card stock paper.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-grid.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-grid-diffuser-test.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Using the CircuitPython LED animations library, it was relatively easy to try out a variety of different LED matrix animation patterns. Building on the primitives in the library, I created additional functionality that supports &lt;a href=&#34;https://starkravingfinkle.org/posts/2021/12/circuitpython-leds-and-animations/&#34;&gt;text-based and sprite-based animations&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/animation_extras.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-complete-748x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;With so many different patterns, sprites, and options to manage—I decided to use a web-based UI to handle the experience.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-webui-display-473x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/led-matrix-webui-settings-473x1024.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The UI is served from the device itself. This turned out to be more challenging than you might think. I&amp;rsquo;ll do a separate post on serving a web-based UI while also running LED animations on the device.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Project: LED Fiber Optic Lamp</title>
				<link>https://starkravingfinkle.org/posts/2022/01/project-led-fiber-optic-lamp/</link>
				<pubDate>Mon, 10 Jan 2022 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2022/01/project-led-fiber-optic-lamp/</guid>
				<description>&lt;p&gt;Looking back at one of the first real projects I attempted which combined 3D printing and microprocessors. I received an &lt;a href=&#34;https://www.creality3dofficial.com/products/ender-3-v2-3d-printer&#34;&gt;Creality Ender 3 V2&lt;/a&gt; a year ago and after playing around with some test prints, I wanted to try building some more interesting and complex projects. I came across this &lt;a href=&#34;https://www.instructables.com/Fiber-Optic-LED-Lamp/&#34;&gt;fiber optic LED lamp&lt;/a&gt; project via &lt;a href=&#34;https://www.instructables.com&#34;&gt;Instructables&lt;/a&gt;. It was just the right amount of 3D printing, microprocessors, and coding I was looking for at the time.&lt;/p&gt;&#xA;&lt;p&gt;I tend to use components from Adafruit. They have a strong focus on learning. The guides and CircuitPython are great for getting started. So given the great set of instructions, my challenge was basically recreating the 3D models and porting to an Adafruit microprocessor running CircuitPython. The author already provided the 3D models as STLs and in &lt;a href=&#34;https://www.tinkercad.com&#34;&gt;Tinkercad&lt;/a&gt; (I also really like the simplicity of Tinkercad), but I wanted to reduce the number of fiber strands and make the lamp slightly smaller.&lt;/p&gt;&#xA;&lt;p&gt;I figured out the general structure and process of the original model pieces by investigating the Tinkercad project. It didn&amp;rsquo;t take long to recreate some shapes that I could use to build the lamp structure.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-models-1024x427.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;CircuitPython has great support for individually addressable &lt;a href=&#34;https://www.amazon.com/dp/B01MG49QKD/ref=syn_sd_onsite_desktop_312&#34;&gt;WS2818 / NeoPixel strands&lt;/a&gt;, so it was relatively simple to get some code working that would create some simple LED animations. I was using a &lt;a href=&#34;https://www.adafruit.com/product/3500&#34;&gt;Trinket M0&lt;/a&gt;, which is so tiny. I ran into some space issues where I couldn&amp;rsquo;t add all the animation support code I wanted onto the board. If I ever revisit this project, I&amp;rsquo;ll probably switch to a &lt;a href=&#34;https://www.adafruit.com/product/4900&#34;&gt;QT Py RP2040&lt;/a&gt; or &lt;a href=&#34;https://www.adafruit.com/product/5325&#34;&gt;QT Py ESP32-S2&lt;/a&gt;, both of which have plenty of space, way more power, and the ESP32-S2 board would even allow for some network/web configuration UX.&lt;/p&gt;&#xA;&lt;p&gt;Here are a few photos of the assembly process of the LEDs and optic fiber in the frame.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-frame.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-led-wired.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-fitting.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-assembling.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-assembled.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://www.amazon.com/gp/product/B07R6C2CC5/ref=ppx_yo_dt_b_asin_title_o01_s00&#34;&gt;fiber optic cable&lt;/a&gt; is a &amp;ldquo;side glow&amp;rdquo; type used for decorations. It&amp;rsquo;s designed to create a glow.&lt;/p&gt;&#xA;&lt;p&gt;Here is the base with the wires and a breadboard for the Trinket M0 (not inserted yet), along with a small button which can be used to change the animation modes.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-base-wired.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The CircuitPython code is very simple and is available in a Github &lt;a href=&#34;https://github.com/mfinkle/led_fiber_optic_lamp&#34;&gt;repo&lt;/a&gt;. I&amp;rsquo;m pretty happy with the finished project. Some things I&amp;rsquo;d want to address if I decide to work on a revision:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Using CircuitPython doesn&amp;rsquo;t leave much room for user code on the Trinket M0, so I&amp;rsquo;d probably just bump up to one of the newer QT Py models. I&amp;rsquo;ll be able to add more animation modes too.&lt;/li&gt;&#xA;&lt;li&gt;Hot gluing the breadboard into the base isn&amp;rsquo;t sturdy enough. I&amp;rsquo;ll need to attach the next board with screws/nuts.&lt;/li&gt;&#xA;&lt;li&gt;Selecting animation modes using the button is not very friendly. If I bump up to a QT Py ESP32-S2, I&amp;rsquo;ll add a web setup UI.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;Looking back at one of the first real projects I attempted which combined 3D printing and microprocessors. I received an &lt;a href=&#34;https://www.creality3dofficial.com/products/ender-3-v2-3d-printer&#34;&gt;Creality Ender 3 V2&lt;/a&gt; a year ago and after playing around with some test prints, I wanted to try building some more interesting and complex projects. I came across this &lt;a href=&#34;https://www.instructables.com/Fiber-Optic-LED-Lamp/&#34;&gt;fiber optic LED lamp&lt;/a&gt; project via &lt;a href=&#34;https://www.instructables.com&#34;&gt;Instructables&lt;/a&gt;. It was just the right amount of 3D printing, microprocessors, and coding I was looking for at the time.&lt;/p&gt;&#xA;&lt;p&gt;I tend to use components from Adafruit. They have a strong focus on learning. The guides and CircuitPython are great for getting started. So given the great set of instructions, my challenge was basically recreating the 3D models and porting to an Adafruit microprocessor running CircuitPython. The author already provided the 3D models as STLs and in &lt;a href=&#34;https://www.tinkercad.com&#34;&gt;Tinkercad&lt;/a&gt; (I also really like the simplicity of Tinkercad), but I wanted to reduce the number of fiber strands and make the lamp slightly smaller.&lt;/p&gt;&#xA;&lt;p&gt;I figured out the general structure and process of the original model pieces by investigating the Tinkercad project. It didn&amp;rsquo;t take long to recreate some shapes that I could use to build the lamp structure.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-models-1024x427.png&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;CircuitPython has great support for individually addressable &lt;a href=&#34;https://www.amazon.com/dp/B01MG49QKD/ref=syn_sd_onsite_desktop_312&#34;&gt;WS2818 / NeoPixel strands&lt;/a&gt;, so it was relatively simple to get some code working that would create some simple LED animations. I was using a &lt;a href=&#34;https://www.adafruit.com/product/3500&#34;&gt;Trinket M0&lt;/a&gt;, which is so tiny. I ran into some space issues where I couldn&amp;rsquo;t add all the animation support code I wanted onto the board. If I ever revisit this project, I&amp;rsquo;ll probably switch to a &lt;a href=&#34;https://www.adafruit.com/product/4900&#34;&gt;QT Py RP2040&lt;/a&gt; or &lt;a href=&#34;https://www.adafruit.com/product/5325&#34;&gt;QT Py ESP32-S2&lt;/a&gt;, both of which have plenty of space, way more power, and the ESP32-S2 board would even allow for some network/web configuration UX.&lt;/p&gt;&#xA;&lt;p&gt;Here are a few photos of the assembly process of the LEDs and optic fiber in the frame.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-frame.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-led-wired.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-fitting.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-assembling.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-fiber-assembled.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;The &lt;a href=&#34;https://www.amazon.com/gp/product/B07R6C2CC5/ref=ppx_yo_dt_b_asin_title_o01_s00&#34;&gt;fiber optic cable&lt;/a&gt; is a &amp;ldquo;side glow&amp;rdquo; type used for decorations. It&amp;rsquo;s designed to create a glow.&lt;/p&gt;&#xA;&lt;p&gt;Here is the base with the wires and a breadboard for the Trinket M0 (not inserted yet), along with a small button which can be used to change the animation modes.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/lamp-base-wired.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The CircuitPython code is very simple and is available in a Github &lt;a href=&#34;https://github.com/mfinkle/led_fiber_optic_lamp&#34;&gt;repo&lt;/a&gt;. I&amp;rsquo;m pretty happy with the finished project. Some things I&amp;rsquo;d want to address if I decide to work on a revision:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Using CircuitPython doesn&amp;rsquo;t leave much room for user code on the Trinket M0, so I&amp;rsquo;d probably just bump up to one of the newer QT Py models. I&amp;rsquo;ll be able to add more animation modes too.&lt;/li&gt;&#xA;&lt;li&gt;Hot gluing the breadboard into the base isn&amp;rsquo;t sturdy enough. I&amp;rsquo;ll need to attach the next board with screws/nuts.&lt;/li&gt;&#xA;&lt;li&gt;Selecting animation modes using the button is not very friendly. If I bump up to a QT Py ESP32-S2, I&amp;rsquo;ll add a web setup UI.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>CircuitPython, LEDs, and Animations</title>
				<link>https://starkravingfinkle.org/posts/2021/12/circuitpython-leds-and-animations/</link>
				<pubDate>Sun, 05 Dec 2021 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2021/12/circuitpython-leds-and-animations/</guid>
				<description>&lt;p&gt;I&amp;rsquo;ve been playing around with some WS2818 / NeoPixel LED 8x8 grids and &lt;a href=&#34;https://circuitpython.org/&#34;&gt;CircuitPython&lt;/a&gt;. The CircuitPython ecosystem is really rich and &lt;a href=&#34;https://www.adafruit.com/&#34;&gt;Adafruit&lt;/a&gt; makes some very handy support libraries. I was using the &lt;a href=&#34;https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation&#34;&gt;LED Animation library&lt;/a&gt; to create some patterns on the neopixel grid, but wanted to try adding more capabilities. The time-slicing approach made it nice to add other code without blocking the program executing while the animations were happening.&lt;/p&gt;&#xA;&lt;p&gt;In particular, I wanted to add bitmap sprite animations and text scrolling. There are great libraries and examples in Adafruit&amp;rsquo;s collection of &lt;a href=&#34;https://learn.adafruit.com/&#34;&gt;Learn&lt;/a&gt; tutorials, but I didn&amp;rsquo;t see anything that played well with the time-slicing. I took a crack at building some of my own support.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/mfinkle/circuitpython_animationextras&#34;&gt;Animation Extras&lt;/a&gt; is a couple of simple code helpers that add bitmap sprite and text scrolling support by building on the LED Animation library.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/animation_extras.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The LED Animation library has some slick ways of grouping individual pixels to create patterns. I added helpers to create the rectangular animation pattern based on those grouping primitives. Checkout the &lt;a href=&#34;https://github.com/mfinkle/circuitpython_animationextras&#34;&gt;repo&lt;/a&gt; for some example usage.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I&amp;rsquo;ve been playing around with some WS2818 / NeoPixel LED 8x8 grids and &lt;a href=&#34;https://circuitpython.org/&#34;&gt;CircuitPython&lt;/a&gt;. The CircuitPython ecosystem is really rich and &lt;a href=&#34;https://www.adafruit.com/&#34;&gt;Adafruit&lt;/a&gt; makes some very handy support libraries. I was using the &lt;a href=&#34;https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation&#34;&gt;LED Animation library&lt;/a&gt; to create some patterns on the neopixel grid, but wanted to try adding more capabilities. The time-slicing approach made it nice to add other code without blocking the program executing while the animations were happening.&lt;/p&gt;&#xA;&lt;p&gt;In particular, I wanted to add bitmap sprite animations and text scrolling. There are great libraries and examples in Adafruit&amp;rsquo;s collection of &lt;a href=&#34;https://learn.adafruit.com/&#34;&gt;Learn&lt;/a&gt; tutorials, but I didn&amp;rsquo;t see anything that played well with the time-slicing. I took a crack at building some of my own support.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/mfinkle/circuitpython_animationextras&#34;&gt;Animation Extras&lt;/a&gt; is a couple of simple code helpers that add bitmap sprite and text scrolling support by building on the LED Animation library.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/animation_extras.gif&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The LED Animation library has some slick ways of grouping individual pixels to create patterns. I added helpers to create the rectangular animation pattern based on those grouping primitives. Checkout the &lt;a href=&#34;https://github.com/mfinkle/circuitpython_animationextras&#34;&gt;repo&lt;/a&gt; for some example usage.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Tracking Work is Fundamental</title>
				<link>https://starkravingfinkle.org/posts/2021/10/tracking-work-is-fundamental/</link>
				<pubDate>Mon, 18 Oct 2021 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2021/10/tracking-work-is-fundamental/</guid>
				<description>&lt;p&gt;&amp;ldquo;Developers should only need Github Issues and Pull Requests to do their job&amp;rdquo; — Why should anyone need more than that to track work?&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/stack-of-rocks-1024x310.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Small companies and startups have small engineering teams. The amount of effort required to understand the ongoing and planned work is low due to sheer lack of ability to take on too much and succeed. Failure weeds out the companies that take on too much, too soon.&lt;/p&gt;&#xA;&lt;p&gt;Companies succeed and grow, and so do the engineering teams. At some point, multiple engineering teams are created or evolve. Ideally, these teams are self-sufficient and isolated from each other, creating modular and decoupled output. This ideal state rarely lasts and soon cross-team projects start to appear. Teams continue to evolve into product and platform functions, creating more opportunities for cross-team dependencies.&lt;/p&gt;&#xA;&lt;p&gt;At this point, work can no longer be tracked at the developer-level alone. Success requires collaboration and coordination. Companies without a cohesive work tracking system that can span individual teams start to slow down. Requirements and dependencies become difficult to track and are often not meeting expectations which lead to rework and churn. Deliverables aren&amp;rsquo;t meeting the guesstimate timelines and drag on.&lt;/p&gt;&#xA;&lt;p&gt;Making work visible is a core attribute to many different methodologies and processes, even the ad-hoc ones. If you don&amp;rsquo;t have a bird&amp;rsquo;s eye view of the engineering work happening at your company, what can you say about your situation? Very little. Try to ascertain the status of a given cross-team project without asking someone. If it takes you longer than 5 minutes, you&amp;rsquo;re in trouble and the people you would have asked don&amp;rsquo;t really know either. All of this work required to figure out a project status is wasting people&amp;rsquo;s time.&lt;/p&gt;&#xA;&lt;p&gt;Work tracking is something that isn&amp;rsquo;t hard to introduce and provides value. It doesn&amp;rsquo;t require adding any extra work for developers, but starts to also provide value to team leads, project managers, and senior leadership.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;Not Jira!&amp;rdquo; The cry goes out across engineering. It doesn&amp;rsquo;t need to be Jira, but don&amp;rsquo;t hate a tool for being successful at what it does. Just because most companies don&amp;rsquo;t put enough effort into running Jira well doesn&amp;rsquo;t make work tracking tools bad in general. Pick something else — except fucking spreadsheets!&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;You die a hero &lt;em&gt;lightweight tool&lt;/em&gt;, or you live long enough to become the villain &lt;em&gt;bloated enterprise-ready system&lt;/em&gt;&amp;rdquo;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;See also: &lt;a href=&#34;https://starkravingfinkle.org/posts/2016/08/on-the-merits-of-bug-tracking/&#34;&gt;Merits of Bug Tracking&lt;/a&gt;&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;&amp;ldquo;Developers should only need Github Issues and Pull Requests to do their job&amp;rdquo; — Why should anyone need more than that to track work?&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;images/stack-of-rocks-1024x310.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Small companies and startups have small engineering teams. The amount of effort required to understand the ongoing and planned work is low due to sheer lack of ability to take on too much and succeed. Failure weeds out the companies that take on too much, too soon.&lt;/p&gt;&#xA;&lt;p&gt;Companies succeed and grow, and so do the engineering teams. At some point, multiple engineering teams are created or evolve. Ideally, these teams are self-sufficient and isolated from each other, creating modular and decoupled output. This ideal state rarely lasts and soon cross-team projects start to appear. Teams continue to evolve into product and platform functions, creating more opportunities for cross-team dependencies.&lt;/p&gt;&#xA;&lt;p&gt;At this point, work can no longer be tracked at the developer-level alone. Success requires collaboration and coordination. Companies without a cohesive work tracking system that can span individual teams start to slow down. Requirements and dependencies become difficult to track and are often not meeting expectations which lead to rework and churn. Deliverables aren&amp;rsquo;t meeting the guesstimate timelines and drag on.&lt;/p&gt;&#xA;&lt;p&gt;Making work visible is a core attribute to many different methodologies and processes, even the ad-hoc ones. If you don&amp;rsquo;t have a bird&amp;rsquo;s eye view of the engineering work happening at your company, what can you say about your situation? Very little. Try to ascertain the status of a given cross-team project without asking someone. If it takes you longer than 5 minutes, you&amp;rsquo;re in trouble and the people you would have asked don&amp;rsquo;t really know either. All of this work required to figure out a project status is wasting people&amp;rsquo;s time.&lt;/p&gt;&#xA;&lt;p&gt;Work tracking is something that isn&amp;rsquo;t hard to introduce and provides value. It doesn&amp;rsquo;t require adding any extra work for developers, but starts to also provide value to team leads, project managers, and senior leadership.&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;Not Jira!&amp;rdquo; The cry goes out across engineering. It doesn&amp;rsquo;t need to be Jira, but don&amp;rsquo;t hate a tool for being successful at what it does. Just because most companies don&amp;rsquo;t put enough effort into running Jira well doesn&amp;rsquo;t make work tracking tools bad in general. Pick something else — except fucking spreadsheets!&lt;/p&gt;&#xA;&lt;p&gt;&amp;ldquo;You die a hero &lt;em&gt;lightweight tool&lt;/em&gt;, or you live long enough to become the villain &lt;em&gt;bloated enterprise-ready system&lt;/em&gt;&amp;rdquo;&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;See also: &lt;a href=&#34;https://starkravingfinkle.org/posts/2016/08/on-the-merits-of-bug-tracking/&#34;&gt;Merits of Bug Tracking&lt;/a&gt;&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Continuously Doing a Thing</title>
				<link>https://starkravingfinkle.org/posts/2021/07/continuously-doing-a-thing/</link>
				<pubDate>Sat, 03 Jul 2021 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2021/07/continuously-doing-a-thing/</guid>
				<description>&lt;blockquote&gt;&#xA;&lt;p&gt;Practice makes perfect — Anonymous Parent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A theme that keeps popping up in my world is the idea of &lt;em&gt;how&lt;/em&gt; &lt;strong&gt;&lt;em&gt;often&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;an action is done&lt;/em&gt; being correlated to &lt;em&gt;how&lt;/em&gt; &lt;strong&gt;&lt;em&gt;well&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;the action is done&lt;/em&gt;. &lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Deploying application and system code&lt;/li&gt;&#xA;&lt;li&gt;Releasing application distributions&lt;/li&gt;&#xA;&lt;li&gt;Triaging issues&lt;/li&gt;&#xA;&lt;li&gt;Testing product behavior&lt;/li&gt;&#xA;&lt;li&gt;Creating objectives&lt;/li&gt;&#xA;&lt;li&gt;Running experiments&lt;/li&gt;&#xA;&lt;li&gt;Executing migrations&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;A lot has been written about high-performing engineering teams. &lt;a href=&#34;https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339&#34;&gt;Accelerate&lt;/a&gt; is a great resource for exploring the behaviors of such teams. Frequent deploys is one of the leading indicators and was chosen by the authors as a key metric. With enough practice, deploys become low-risk and low-stress.&lt;/p&gt;&#xA;&lt;p&gt;Small batches are another trait of successful teams. Performing a deployment more frequently usually means there are fewer changes happening each time. These small batches can actually improve overall quality because fewer changes happen in each cycle.&lt;/p&gt;&#xA;&lt;p&gt;Rotating a large group of people through activity shifts, like handling issue triage or the application release process, allows the group to share the burden, but there are downsides too. If the activity isn&amp;rsquo;t part of the group&amp;rsquo;s primary deliverable, it&amp;rsquo;s likely not a priority. If there are long stretches of time between any given person taking on the activity there might only be enough time to just do the work, but never think about how to improve the process or tooling. There is no time to become good at the process.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.amazon.com/DevOps-Handbook-World-Class-Reliability-Organizations/dp/1942788002&#34;&gt;The DevOps Handbook&lt;/a&gt; talks a lot about the benefits of shorter feedback loops across many different aspects of engineering organizations. In most situations, shorter feedback loops happen when an activity becomes a more continuous process.&lt;/p&gt;&#xA;&lt;p&gt;If you have an area that could be improved, maybe you could ask yourself if the process could happen more often.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;blockquote&gt;&#xA;&lt;p&gt;Practice makes perfect — Anonymous Parent&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;A theme that keeps popping up in my world is the idea of &lt;em&gt;how&lt;/em&gt; &lt;strong&gt;&lt;em&gt;often&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;an action is done&lt;/em&gt; being correlated to &lt;em&gt;how&lt;/em&gt; &lt;strong&gt;&lt;em&gt;well&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;the action is done&lt;/em&gt;. &lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Deploying application and system code&lt;/li&gt;&#xA;&lt;li&gt;Releasing application distributions&lt;/li&gt;&#xA;&lt;li&gt;Triaging issues&lt;/li&gt;&#xA;&lt;li&gt;Testing product behavior&lt;/li&gt;&#xA;&lt;li&gt;Creating objectives&lt;/li&gt;&#xA;&lt;li&gt;Running experiments&lt;/li&gt;&#xA;&lt;li&gt;Executing migrations&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;A lot has been written about high-performing engineering teams. &lt;a href=&#34;https://www.amazon.com/Accelerate-Software-Performing-Technology-Organizations/dp/1942788339&#34;&gt;Accelerate&lt;/a&gt; is a great resource for exploring the behaviors of such teams. Frequent deploys is one of the leading indicators and was chosen by the authors as a key metric. With enough practice, deploys become low-risk and low-stress.&lt;/p&gt;&#xA;&lt;p&gt;Small batches are another trait of successful teams. Performing a deployment more frequently usually means there are fewer changes happening each time. These small batches can actually improve overall quality because fewer changes happen in each cycle.&lt;/p&gt;&#xA;&lt;p&gt;Rotating a large group of people through activity shifts, like handling issue triage or the application release process, allows the group to share the burden, but there are downsides too. If the activity isn&amp;rsquo;t part of the group&amp;rsquo;s primary deliverable, it&amp;rsquo;s likely not a priority. If there are long stretches of time between any given person taking on the activity there might only be enough time to just do the work, but never think about how to improve the process or tooling. There is no time to become good at the process.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.amazon.com/DevOps-Handbook-World-Class-Reliability-Organizations/dp/1942788002&#34;&gt;The DevOps Handbook&lt;/a&gt; talks a lot about the benefits of shorter feedback loops across many different aspects of engineering organizations. In most situations, shorter feedback loops happen when an activity becomes a more continuous process.&lt;/p&gt;&#xA;&lt;p&gt;If you have an area that could be improved, maybe you could ask yourself if the process could happen more often.&lt;/p&gt;&#xA;</content:encoded>
			</item>
			<item>
				<title>Being an Effective Engineering Leader</title>
				<link>https://starkravingfinkle.org/posts/2020/07/being-an-effective-engineering-leader/</link>
				<pubDate>Sat, 25 Jul 2020 00:00:00 +0000</pubDate>
				<guid>https://starkravingfinkle.org/posts/2020/07/being-an-effective-engineering-leader/</guid>
				<description>&lt;p&gt;I often wonder if I&amp;rsquo;m being effective at my job. Might be related to my impostor syndrome, but in engineering management, the signals of effectiveness aren&amp;rsquo;t always clear. I have some basic, high level criteria I try to think about monthly, or so, to provide some insight.&lt;/p&gt;&#xA;&lt;h2 id=&#34;providing-a-clear-direction&#34;&gt;Providing a clear direction&lt;/h2&gt;&#xA;&lt;p&gt;Lack of clear direction can sometimes be seen when teams are doing medium-term/quarterly planning. If the objectives aren&amp;rsquo;t aligned with upper management, it&amp;rsquo;s probably my fault for not creating clear direction and expected outcomes. Try not to be too prescriptive, but make sure the goals are clearly defined.&lt;/p&gt;&#xA;&lt;p&gt;Try to have a good narrative for each of these levels:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Vision:&lt;/strong&gt; How the team(s) create impact&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mission:&lt;/strong&gt; Role the team(s) within the company&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Objectives:&lt;/strong&gt; Think about the next year&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;shipping-what-matters&#34;&gt;Shipping what matters&lt;/h2&gt;&#xA;&lt;p&gt;Do the same problems keep coming up? Make sure we are prioritizing the right work. Make sure we are completing the work. Talk to senior engineers about problems that seem to be holding us back.&lt;/p&gt;&#xA;&lt;p&gt;Focus on capabilities. Keep improving the operational capabilities of the company. Feature projects are built on capabilities.&lt;/p&gt;&#xA;&lt;p&gt;Maintain a healthy mix of project sizes. Big projects can stall shipping momentum. Make sure big projects are broken into smaller milestones and iterations. Small projects might feel like low impact, but sometimes are just what people are asking to see.&lt;/p&gt;&#xA;&lt;h2 id=&#34;helping-people-grow&#34;&gt;Helping people grow&lt;/h2&gt;&#xA;&lt;p&gt;Have honest conversations about expectations and performance, and providing actionable feedback.&lt;/p&gt;&#xA;&lt;p&gt;Make space for other people by getting out of the way. For projects and meetings where I&amp;rsquo;m getting invited as a point-of-contact, look for other people I can delegate the role to.&lt;/p&gt;&#xA;&lt;h2 id=&#34;surveys-and-feedback-loops&#34;&gt;Surveys and Feedback Loops&lt;/h2&gt;&#xA;&lt;p&gt;Workplaces typically have company-wide engagement surveys to get feedback on many aspects. Those usually have a management section, and this feedback can be a gift. Interpreting feedback in a positive a way and not a personal attack might be a learned skill, but worth learning.&lt;/p&gt;&#xA;&lt;p&gt;Thanks to Nick DiStefano for a reminder that manager surveys are also a useful way to get regular feedback on how things are going. Manager surveys can happen more frequently than company-wide engagement surveys and are usually more focused at the team-level.&lt;/p&gt;&#xA;</description>
				<content:encoded>&lt;p&gt;I often wonder if I&amp;rsquo;m being effective at my job. Might be related to my impostor syndrome, but in engineering management, the signals of effectiveness aren&amp;rsquo;t always clear. I have some basic, high level criteria I try to think about monthly, or so, to provide some insight.&lt;/p&gt;&#xA;&lt;h2 id=&#34;providing-a-clear-direction&#34;&gt;Providing a clear direction&lt;/h2&gt;&#xA;&lt;p&gt;Lack of clear direction can sometimes be seen when teams are doing medium-term/quarterly planning. If the objectives aren&amp;rsquo;t aligned with upper management, it&amp;rsquo;s probably my fault for not creating clear direction and expected outcomes. Try not to be too prescriptive, but make sure the goals are clearly defined.&lt;/p&gt;&#xA;&lt;p&gt;Try to have a good narrative for each of these levels:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Vision:&lt;/strong&gt; How the team(s) create impact&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Mission:&lt;/strong&gt; Role the team(s) within the company&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Objectives:&lt;/strong&gt; Think about the next year&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;shipping-what-matters&#34;&gt;Shipping what matters&lt;/h2&gt;&#xA;&lt;p&gt;Do the same problems keep coming up? Make sure we are prioritizing the right work. Make sure we are completing the work. Talk to senior engineers about problems that seem to be holding us back.&lt;/p&gt;&#xA;&lt;p&gt;Focus on capabilities. Keep improving the operational capabilities of the company. Feature projects are built on capabilities.&lt;/p&gt;&#xA;&lt;p&gt;Maintain a healthy mix of project sizes. Big projects can stall shipping momentum. Make sure big projects are broken into smaller milestones and iterations. Small projects might feel like low impact, but sometimes are just what people are asking to see.&lt;/p&gt;&#xA;&lt;h2 id=&#34;helping-people-grow&#34;&gt;Helping people grow&lt;/h2&gt;&#xA;&lt;p&gt;Have honest conversations about expectations and performance, and providing actionable feedback.&lt;/p&gt;&#xA;&lt;p&gt;Make space for other people by getting out of the way. For projects and meetings where I&amp;rsquo;m getting invited as a point-of-contact, look for other people I can delegate the role to.&lt;/p&gt;&#xA;&lt;h2 id=&#34;surveys-and-feedback-loops&#34;&gt;Surveys and Feedback Loops&lt;/h2&gt;&#xA;&lt;p&gt;Workplaces typically have company-wide engagement surveys to get feedback on many aspects. Those usually have a management section, and this feedback can be a gift. Interpreting feedback in a positive a way and not a personal attack might be a learned skill, but worth learning.&lt;/p&gt;&#xA;&lt;p&gt;Thanks to Nick DiStefano for a reminder that manager surveys are also a useful way to get regular feedback on how things are going. Manager surveys can happen more frequently than company-wide engagement surveys and are usually more focused at the team-level.&lt;/p&gt;&#xA;</content:encoded>
			</item>
	</channel>
</rss>
