<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Microservice architecture</title>
        <description>A pattern language for microservices</description>
        <link>http://microservices.io</link>
        <atom:link href="http://microservices.io/feed.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>Architecting for uncertainty - part 3: GenAI as an amplifier</title>
            <description>&lt;p&gt;This is the third in a series of articles based on my talk &lt;em&gt;Architecting for Fast Flow: Thriving Amid Uncertainty&lt;/em&gt;.
In this series, I define the term &lt;em&gt;fast flow&lt;/em&gt; and explain why it has become an essential software delivery capability.
I discuss how fast flow emerges from a &lt;em&gt;socio-technical architecture&lt;/em&gt;, which consists of a software architecture and its development organization.
Finally, I describe how to design and evolve a socio-technical architecture in order to sustain fast flow.&lt;/p&gt;

&lt;p&gt;The articles in the series are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-1-thriving-in-a-vuca-world-requires-frequent-deployment/&quot;&gt;Thriving in a VUCA world requires frequent deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-2-fast-flow-defined/&quot;&gt;Fast flow defined&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-3-genai-as-an-amplifier&quot;&gt;GenAI as an amplifier&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-2-fast-flow-defined/&quot;&gt;Part 2&lt;/a&gt; explained that an organization that wants to thrive in a VUCA world must practice fast flow: deliver a continuous stream of small changes to production, and continually learn from feedback from users and from production.
Given all the hype, you might think that fast flow can be achieved by simply using GenAI coding agents.
This article explains why GenAI alone cannot guarantee fast flow, and how it amplifies an organization’s existing software delivery capabilities, for better or for worse.&lt;/p&gt;

&lt;p style=&quot;font-size: 1.25em; font-weight: bold;&quot;&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-3-genai-as-an-amplifier/&quot;&gt;Read more (This post is for paying subscribers only)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/architecting-for-an-uncertain-world/part-3/genai-is-an-amplifier.png&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Wed, 07 Oct 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/10/07/architecting-for-uncertainty-part-3.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/10/07/architecting-for-uncertainty-part-3.html</guid>
        </item>
        
        <item>
            <title>Architecting with LLMs: use plain old code when you can, LLMs when you must</title>
            <description>&lt;p&gt;I recently wrote about the &lt;a href=&quot;/post/architecture/2026/05/19/genai-development-platform-part-4-coding-agent-sandwich.html&quot;&gt;Coding agent sandwich&lt;/a&gt;, which is an architecture for a coding agent harness.
The architecture consists of a tasty filling of LLM invocations sandwiched between two slices of plain old code (POC).
Its practical rule is: use POC when you can, LLMs when you must.&lt;/p&gt;

&lt;p&gt;The following diagram shows how that article’s worked example, the &lt;a href=&quot;/post/architecture/2026/03/17/overview-i2code-implement-agent-orchestrator.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implement plan&lt;/code&gt; workflow&lt;/a&gt;, applies this rule.
The workflow is decomposed into a tree of responsibilities.
Each responsibility is implemented using either POC (gray) or an LLM (yellow).
LLMs are used only for the responsibilities that require judgment, such as writing code and fixing a broken build.
The workflow has orchestration at two levels.
POC orchestrates the overall workflow, and the LLM agent that implements each task decides which action to take next within that task.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/llms/functional-decomposition-tree.svg&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In this article, I apply that rule and the other ideas from the Coding agent sandwich to architecting systems that leverage LLMs.
Let’s start by looking at the timeless idea of the right tool for the right job.&lt;/p&gt;

&lt;h2 id=&quot;the-right-tool-for-the-right-job&quot;&gt;The right tool for the right job&lt;/h2&gt;

&lt;p&gt;As with many new technologies, GenAI agents have attracted tremendous hype.
This naturally leads to the Golden Hammer anti-pattern: the tendency to apply a shiny tool to every problem, regardless of whether it’s the best fit.
As the hype settles, it becomes clearer where agents fit and where POC is the better choice.&lt;/p&gt;

&lt;h2 id=&quot;when-to-use-poc&quot;&gt;When to use POC&lt;/h2&gt;

&lt;p&gt;POC is the right choice when you can specify how to compute the answer.
This is the case when the rules are known and correctness can be precisely defined.
Don’t &lt;a href=&quot;/post/architecture/2026/01/28/when-two-problems-becomes-four-genai-based-development.html&quot;&gt;waste your time trying to solve a problem with an LLM&lt;/a&gt; when a far simpler solution exists.&lt;/p&gt;

&lt;p&gt;Beyond being the right fit for these problems, POC has the benefit of repeatability.
Given the same inputs and state, POC behaves the same way every time, while an LLM is probabilistic.
Repeatability also makes POC easier to develop and debug because failures are reproducible.
There’s no need to &lt;a href=&quot;/post/architecture/2026/03/01/using-genai-based-coding-agents-cynefin-complex-domain.html&quot;&gt;experiment&lt;/a&gt; with different prompts and hope that the LLM does the right thing.
And at runtime, POC is typically much faster and cheaper than an LLM invocation.&lt;/p&gt;

&lt;h2 id=&quot;when-to-use-an-llm&quot;&gt;When to use an LLM&lt;/h2&gt;

&lt;p&gt;An LLM is a good candidate when you can specify what a good answer looks like but cannot reasonably specify how to compute it.
This is the case when the input is ambiguous or unstructured, when understanding natural language is required, or when producing the answer requires judgment or interpretation.
For such problems, enumerating deterministic rules is impractical.&lt;/p&gt;

&lt;p&gt;Here are some real-world examples where an LLM is a good candidate:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Interpreting an ambiguous operational support request&lt;/li&gt;
  &lt;li&gt;Translating a user’s business intent into an enterprise transaction&lt;/li&gt;
  &lt;li&gt;Interpreting complex contractual or engineering requirements&lt;/li&gt;
  &lt;li&gt;Implementing a software change from a natural-language specification&lt;/li&gt;
  &lt;li&gt;Diagnosing an unfamiliar production failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These capabilities come at a price.
An LLM invocation is slow and expensive compared to executing POC.
And because an LLM is probabilistic, repeated invocations with the same input can behave differently.
So before you commit to an LLM, test it against representative examples to check that its error rate, cost, and latency are acceptable.&lt;/p&gt;

&lt;h2 id=&quot;start-with-functional-decomposition&quot;&gt;Start with functional decomposition&lt;/h2&gt;

&lt;p&gt;Designing a system that uses LLMs starts with recursive functional decomposition.
You identify the responsibilities needed to solve the problem.
These responsibilities include orchestration, which is the control flow that decides which responsibility to execute next.&lt;/p&gt;

&lt;p&gt;The decomposition is hierarchical.
A responsibility X can itself be decomposed into sub-responsibilities X1 and X2, together with a coordinator that orchestrates them.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/llms/functional-decomposition.svg&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For example, consider the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implement plan&lt;/code&gt; workflow that is the worked example in the Coding agent sandwich article.
Its job is to turn each task in a development plan into a Git commit.
Decomposing this problem identifies seven responsibilities:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Getting the next task from the plan&lt;/li&gt;
  &lt;li&gt;Implementing the task&lt;/li&gt;
  &lt;li&gt;Validating the result&lt;/li&gt;
  &lt;li&gt;Pushing the commit and creating a pull request&lt;/li&gt;
  &lt;li&gt;Waiting for the CI build&lt;/li&gt;
  &lt;li&gt;Fixing the CI build if it fails&lt;/li&gt;
  &lt;li&gt;Addressing pull request review feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementing the task is itself decomposed into writing a test, writing the code, running the tests, marking the task as complete, and committing the changes.
The orchestration responsibility is the loop that executes these steps for each task until no tasks remain.&lt;/p&gt;

&lt;h2 id=&quot;for-each-responsibility-poc-or-llm&quot;&gt;For each responsibility: POC or LLM?&lt;/h2&gt;

&lt;p&gt;The next step is to decide, for each responsibility, whether to implement it using POC or an LLM.
As described earlier, use POC when you can specify how to compute the answer, and use an LLM when you can only specify what a good answer looks like.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/llms/poc-or-llm-decision.svg&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;When assigning a responsibility to an LLM, keep the responsibility narrowly defined.
For example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implement plan&lt;/code&gt; workflow invokes a coding agent for each task in the plan, instead of using one agent run to implement the whole plan.
An LLM invocation or agent run that handles many responsibilities is less reliable.
The LLM might, for example, stop partway through.
A large invocation or run is also wasteful because it uses an expensive LLM for work that POC could do far more cheaply.&lt;/p&gt;

&lt;h2 id=&quot;for-each-llm-responsibility-which-model&quot;&gt;For each LLM responsibility: which model?&lt;/h2&gt;

&lt;p&gt;Once you have decided to implement a responsibility using an LLM, you still need to pick which LLM.
LLMs differ in capability, speed, and cost.
The more capable models also let you adjust how much reasoning they do per invocation, which affects both cost and latency.
A responsibility that requires deep reasoning, such as diagnosing an unfamiliar production failure, needs a large, capable model.
A responsibility that requires little reasoning, such as summarizing a support request, can often use a smaller model that is faster and cheaper.
Because each responsibility is a separate LLM invocation, you can pick the model for each one.
As with the POC-or-LLM decision, test the candidate models against representative examples and compare their error rate, cost, and latency.&lt;/p&gt;

&lt;h2 id=&quot;alternative-machine-learning-models&quot;&gt;Alternative machine learning models&lt;/h2&gt;

&lt;p&gt;An LLM is just one kind of machine learning model.
Use the kind of model whose capability matches the responsibility:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Interpreting natural language - an LLM&lt;/li&gt;
  &lt;li&gt;Finding similar items - an embedding model&lt;/li&gt;
  &lt;li&gt;Predicting whether a transaction is fraudulent - a classifier&lt;/li&gt;
  &lt;li&gt;Predicting tomorrow’s transaction volume - a time-series model&lt;/li&gt;
  &lt;li&gt;Identifying damage in a photograph - a vision model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A specialized model is typically cheaper, faster, and more accurate at its task than an LLM.
The rule therefore generalizes: decompose the system into responsibilities, and implement each one using POC or the machine learning model whose capability matches it.&lt;/p&gt;

&lt;h2 id=&quot;orchestration-poc-or-llm&quot;&gt;Orchestration: POC or LLM?&lt;/h2&gt;

&lt;p&gt;When a system contains an LLM, it’s tempting to assume that the LLM should orchestrate it.
But orchestration is itself a responsibility, so the same POC-or-LLM decision applies to it.
There are two options:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;POC orchestration&lt;/strong&gt; - the right choice when you can define the steps and the branches between them in advance. The POC orchestrator decides which responsibility to execute next. When a branch requires judgment, the orchestrator can invoke an LLM to choose one of the branches that it defines, and then execute it. Making a decision is simply another narrowly defined responsibility that is implemented using an LLM.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LLM-based orchestration&lt;/strong&gt; - the right choice when you cannot define the steps in advance. The LLM examines the current state and the results so far, and decides what to invoke next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implement plan&lt;/code&gt; workflow uses POC orchestration, which forms the top slice of the Coding agent sandwich.&lt;/p&gt;

&lt;h2 id=&quot;an-agent-orchestrates-and-does-the-work&quot;&gt;An agent orchestrates and does the work&lt;/h2&gt;

&lt;p&gt;When an LLM decides each next action after seeing the result of the previous one, the responsibility is implemented as an agent.
An agent consists of an LLM, a harness, and a set of tools.
The LLM also decides when it has finished.
The harness, such as Claude Code, is POC that runs the loop.
It invokes the LLM, executes each tool call that the LLM requests, and passes the result back to the LLM.&lt;/p&gt;

&lt;p&gt;The LLM in an agent does more than orchestrate.
It also performs some of the responsibilities that it orchestrates and delegates the rest to tools.
For example, the coding agent that implements a task writes the tests and the code itself, and it runs the tests using Gradle.
The coding agent forms the filling of the sandwich.&lt;/p&gt;

&lt;h2 id=&quot;give-llms-poc-tools&quot;&gt;Give LLMs POC tools&lt;/h2&gt;

&lt;p&gt;A responsibility implemented using an LLM can itself be decomposed.
When it needs to act on the world, such as running tests, querying a database, or invoking an API, each action is a sub-responsibility whose rules are known.
Implement each one using POC as a tool with well-defined behavior that the LLM invokes.
For lessons on designing such tools, see &lt;a href=&quot;/post/deployment-pipeline/2026/07/17/from-webfetch-to-circleci-cli-lessons-learned.html&quot;&gt;Migrating a Claude Code skill from WebFetch to the new CircleCI CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/llms/llm-deterministic-tools.svg&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For example, the coding agent that implements a task invokes Gradle to run the tests and Git to commit the changes.
The LLM decides what to do next based on the actual test results.
These tools form the bottom slice of the sandwich.&lt;/p&gt;

&lt;h2 id=&quot;llms-often-belong-at-the-boundaries&quot;&gt;LLMs often belong at the boundaries&lt;/h2&gt;

&lt;p&gt;Applying the POC-or-LLM rule to each responsibility tends to put LLMs at the boundaries of a system.
At a boundary, the system receives unstructured, ambiguous input from the real world, which must be interpreted.
Once the input has been interpreted, it becomes structured state that well-defined operations can act on.
Those operations are implemented using POC.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/llms/llm-at-the-boundary.svg&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;For example, consider a system that handles operational support requests.
Each request is written in natural language and is often ambiguous.
An LLM interprets the request and converts it into a structured ticket, which specifies the category, the priority, and the affected service.
A well-formed ticket can still be wrong, because the LLM might misinterpret the request.
POC therefore validates the ticket, for example by checking that the affected service exists, and sends a ticket that fails validation to a person for review.
POC then routes each valid ticket and updates the ticketing system.
Validation can catch a ticket that names a service that doesn’t exist, but it can’t catch one that names the wrong service.&lt;/p&gt;

&lt;p&gt;Not every LLM responsibility is at a boundary.
Each LLM responsibility in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;implement plan&lt;/code&gt; workflow interprets some input, such as a task description, the logs of a failed build, or review comments.
But most of its work is writing code, which requires judgment.&lt;/p&gt;

&lt;h2 id=&quot;revisiting-the-poc-or-llm-decision&quot;&gt;Revisiting the POC-or-LLM decision&lt;/h2&gt;

&lt;p&gt;The decision to implement a responsibility using POC or an LLM can change after the initial implementation.
When you first implement a responsibility, you might not understand the problem well enough to specify how to compute the answer, so you use an LLM.
As your understanding improves, you might find that you can specify the computation after all.
Reimplementing the responsibility using POC then makes it faster, cheaper, and repeatable.&lt;/p&gt;

&lt;p&gt;The change can also go the other way.
A responsibility that you implemented using POC might turn out to require judgment.
For example, its input might be more varied than you expected, so you keep adding rules to handle new cases.
In that case, reimplementing the responsibility using an LLM might be simpler than adding more rules.&lt;/p&gt;

&lt;p&gt;In either direction, compare the two implementations before switching.
Run both against the same representative inputs, and check their results, cost, and latency against the same acceptance criteria.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;The key idea of this article is to design a system that uses LLMs by applying functional decomposition.
You decompose the problem into responsibilities, including orchestration, and then decide how to implement each one.
To avoid the Golden Hammer anti-pattern, make that decision separately for each responsibility.
Use POC whenever you can specify how to compute the answer.
Use a machine learning model only when you must, because you can only specify what a good answer looks like.
Use an LLM when the responsibility requires interpreting natural language or exercising judgment, and a specialized model, such as a classifier, when it doesn’t.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Tue, 29 Sep 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/09/29/architecting-with-llms.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/09/29/architecting-with-llms.html</guid>
        </item>
        
        <item>
            <title>Architecting for uncertainty - part 2: fast flow defined</title>
            <description>&lt;p&gt;This is the second in a series of articles based on my talk &lt;em&gt;Architecting for Fast Flow: Thriving Amid Uncertainty&lt;/em&gt;.
In this series, I define the term &lt;em&gt;fast flow&lt;/em&gt; and explain why it has become an essential software delivery capability.
I discuss how fast flow emerges from a &lt;em&gt;socio-technical architecture&lt;/em&gt;, which consists of a software architecture and its development organization.
Finally, I describe how to design and evolve a socio-technical architecture in order to sustain fast flow.&lt;/p&gt;

&lt;p&gt;The articles in the series are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-1-thriving-in-a-vuca-world-requires-frequent-deployment/&quot;&gt;Thriving in a VUCA world requires frequent deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-2-fast-flow-defined/&quot;&gt;Fast flow defined&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-3-genai-as-an-amplifier&quot;&gt;GenAI as an amplifier&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-1-thriving-in-a-vuca-world-requires-frequent-deployment/&quot;&gt;The first article&lt;/a&gt; explained that thriving in a VUCA world requires a business to constantly experiment, and that the more frequently it deploys changes, the more rapidly it learns.
This article describes the software delivery capability that makes that possible: fast flow.&lt;/p&gt;

&lt;p style=&quot;font-size: 1.25em; font-weight: bold;&quot;&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-2-fast-flow-defined/&quot;&gt;Read more (This post is for paying subscribers only)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/architecting-for-an-uncertain-world/part-2/fast-flow-02.png&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Thu, 03 Sep 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/09/03/architecting-for-uncertainty-part-2.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/09/03/architecting-for-uncertainty-part-2.html</guid>
        </item>
        
        <item>
            <title>Microservices and GenAI in 2026: my Dear Architects conversation</title>
            <description>&lt;p&gt;Earlier this year I was a guest on Luca Mezzalira’s &lt;a href=&quot;https://www.youtube.com/watch?v=0FF5r8ebuZQ&quot;&gt;Dear Architects podcast&lt;/a&gt;.
We talked about where microservices are in 2026 and what GenAI changes.
My take is that the best software development practices we had before GenAI - you could even say extreme programming - are even more necessary now, because of the nature of GenAI.&lt;/p&gt;

&lt;p&gt;Here are the main points.
The full conversation is &lt;a href=&quot;https://www.youtube.com/watch?v=0FF5r8ebuZQ&quot;&gt;on the episode&lt;/a&gt;:&lt;/p&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/0FF5r8ebuZQ&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;h2 id=&quot;we-are-still-building-big-balls-of-mud&quot;&gt;We are still building big balls of mud&lt;/h2&gt;

&lt;p&gt;Painting with a very broad brush, I think far too many enterprises just muddle along and mostly build big balls of mud.&lt;/p&gt;

&lt;h3 id=&quot;software-serves-two-constituencies&quot;&gt;Software serves two constituencies&lt;/h3&gt;

&lt;p&gt;Software is an unusual kind of artifact.
With most things, how you build one today does not affect how you build the next one.
With software, the design decisions you make today affect how easy it is to make changes tomorrow.&lt;/p&gt;

&lt;p&gt;Software also serves two constituencies.
The end users need features.
The organization that builds the software needs it to be easy to change, so that it can keep delivering those features.
The second need is the one that gets neglected.
The emphasis is on feature development to the exclusion of proper architecture and improving the -ilities.
So the software gradually becomes harder to change, and shipping features slows down.
As the requirements grow, an application’s architecture has to be improved so that it stays easy to change.&lt;/p&gt;

&lt;h3 id=&quot;good-practices-get-devoured&quot;&gt;Good practices get devoured&lt;/h3&gt;

&lt;p&gt;The other part of the problem is what organizations do to good practices.
Agile practices came along and got devoured by the organization’s change-resistant antibodies, and morphed into that abomination called SAFe.
Then microservices came along and got devoured as well.&lt;/p&gt;

&lt;h2 id=&quot;the-red-flags-of-a-distributed-monolith&quot;&gt;The red flags of a distributed monolith&lt;/h2&gt;

&lt;p&gt;When I start working with an organization, there are a few red flags I look for:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The ratio of services to developers - For a long time my rule of thumb was one service per team. These days I would say you should only have a service if it solves a problem. A really common anti-pattern is to have almost as many services as developers, or more. I repeatedly see a team of five people with five services, asking how to solve some distributed problem across them. Usually the answer is to merge them together, and then you do not have a distributed problem anymore.&lt;/li&gt;
  &lt;li&gt;A go-faster architecture that is not delivering any faster - Look at your deployment frequency and your lead time.&lt;/li&gt;
  &lt;li&gt;Testing and releasing services in lockstep - 99% of the reason you have services is so that you can deploy them independently, but then the old QA practice of a system release takes over: we have to test them all together and release them in lockstep.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Either way, you are incurring the cost and complexity of microservices without any of the benefit.
They are all symptoms of a distributed monolith.&lt;/p&gt;

&lt;h2 id=&quot;dark-energy-and-dark-matter&quot;&gt;Dark energy and dark matter&lt;/h2&gt;

&lt;p&gt;Your default architecture should be a monolith unless you have a clear, tangible problem that introducing one or more services would actually solve.
So the question is what problems a service would solve, and conversely, what problems it would create.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;img-responsive&quot; src=&quot;/i/posts/dark-energy-dark-matter/Dark_Energy_Dark_Matter_overview.png&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I am a science-fiction nerd who is also into software patterns, and the intersection of the two prompted me to come up with the dark energy and dark matter forces.
They are the criteria for deciding whether two subdomains should go together in the same service or be separated into different services.
I made it up because it sounded really cool, but it turned out to be useful.
Both terms come from astrophysics:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dark energy - the concept astrophysicists use to explain the accelerating expansion of the universe, an anti-gravity that pushes matter apart. It is the metaphor for the forces that push your subdomains apart into separate services. One is the need for a fast deployment pipeline: the more components you have, the smaller each one is, and the faster its deployment pipeline runs. Another is team autonomy: more services means teams can be more autonomous.&lt;/li&gt;
  &lt;li&gt;Dark matter - the invisible matter that makes up most of the universe. You cannot see it, only its gravitational effect. It is the metaphor for the forces that resist decomposition and pull your subdomains together into a monolith. One is minimize runtime coupling: inter-service communication involves latency and can impact availability. Another is prefer ACID over BASE: operations that span multiple services are eventually consistent, not ACID.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are five forces pushing your subdomains apart and five pulling them together.
I described a couple of each on the episode; you can &lt;a href=&quot;/post/architecture/2023/03/26/dark-energy-dark-matter-force-descriptions.html&quot;&gt;read about all ten on microservices.io&lt;/a&gt;.
They are in conflict, and the job of the architect - whether choosing between a monolith and microservices, or defining service boundaries - is to find the set of trade-offs that resolves them.&lt;/p&gt;

&lt;h3 id=&quot;context-matters&quot;&gt;Context matters&lt;/h3&gt;

&lt;p&gt;The strength of each force depends on your context: the complexity of your application and the number of teams you have.
Team autonomy is an excellent thing, but if you only have one team it is not a reason to have multiple services.
Similarly, a small application with a few developers committing regularly has a lightly used deployment pipeline that gives fast feedback.
As the application grows and the number of teams grows, each build takes longer and more commits flow into the pipeline, and eventually it becomes a bottleneck.
That is when you want to go from a monolith with a single deployment pipeline to multiple services, each with its own.&lt;/p&gt;

&lt;h3 id=&quot;low-latency-example&quot;&gt;Low latency example&lt;/h3&gt;

&lt;p&gt;Luca raised the case of a latency-sensitive system where services are ruled out because of the overhead.
Suppose an application has many REST endpoints and only a handful of them are latency sensitive.
You put those operations, and all the code they touch, in the same service so that they are not distributed, and the operations that are not latency sensitive can be distributed.
Low latency might constrain your service boundaries in certain ways, but it does not necessarily force you to use a monolith.&lt;/p&gt;

&lt;h3 id=&quot;the-microservice-architecture-is-unforgiving&quot;&gt;The microservice architecture is unforgiving&lt;/h3&gt;

&lt;p&gt;Where you put things matters more in a microservice architecture.
In a monolith you can write a class and put it anywhere; it does not matter immediately, though modularity and coupling catch up with you later.
In a microservice architecture, where you put things has an immediate impact on latency and coupling, and that is how you can very easily end up with a distributed monolith.&lt;/p&gt;

&lt;h2 id=&quot;there-is-more-to-microservices-than-services&quot;&gt;There is more to microservices than services&lt;/h2&gt;

&lt;p&gt;Delivering software with fast flow takes more than the right architecture.
You also need the right organization and the right process.
Specifically, you need an organization structured according to Team Topologies, DevOps as defined by the DevOps Handbook, and an architecture that enables both.
That is what I call the &lt;a href=&quot;/post/architecture/2024/04/10/success-triangle-is-about-fast-flow.html&quot;&gt;success triangle&lt;/a&gt;.
And that is the role of microservices when you are building at scale: a large organization and a large application.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/fast-flow/SuccessTriangle_Fast_Flow.png&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Luca observed that companies often adopt microservices without changing anything about the socio-technical system around them.
I described this in 2018 as the &lt;a href=&quot;/post/antipatterns/2019/06/07/antipattern-red-flag-law.html&quot;&gt;red flag law anti-pattern&lt;/a&gt;.
In some jurisdictions, a pedestrian waving a red flag and blowing a whistle was required to walk in front of an automobile.
You have a car that could go faster than a person, but rules and regulations slow it down.
That is what exists in a lot of organizations: a go-faster architecture that the organization’s policies, procedures, and structure slow down to pedestrian pace.&lt;/p&gt;

&lt;h2 id=&quot;the-fundamentals-matter-even-more-with-genai&quot;&gt;The fundamentals matter even more with GenAI&lt;/h2&gt;

&lt;p&gt;The practices that make software easy to change matter more now than they did before GenAI.
A coding agent depends on fast feedback and on guardrails.
Both of those come from your architecture and your tests.&lt;/p&gt;

&lt;h3 id=&quot;coding-agents-need-fast-feedback&quot;&gt;Coding agents need fast feedback&lt;/h3&gt;

&lt;p&gt;A coding agent essentially has to use test-driven development.
It needs to be able to quickly run the tests and get feedback, so the inner developer loop has to run exceedingly quickly.
The outer loop has to be fast too: a push triggers the deployment pipeline, and that needs to run quickly and get the change into production, where it gets more feedback.&lt;/p&gt;

&lt;p&gt;The whole motivation for a fast-flow architecture is rapid feedback, so you want to structure your code, your tests, and your deployment pipelines so that your coding agents get prompt feedback.
In that sense, microservices are even more likely to be used today than they were before GenAI.&lt;/p&gt;

&lt;h3 id=&quot;fast-feedback-is-a-property-of-the-architecture&quot;&gt;Fast feedback is a property of the architecture&lt;/h3&gt;

&lt;p&gt;As I wrote in &lt;a href=&quot;/post/architecture/2026/02/08/architecting-for-genai-based-software-delivery.html&quot;&gt;GenAI-based software delivery needs a fast flow architecture&lt;/a&gt;, fast feedback loops are a property of the architecture.
Loose design-time coupling keeps the scope of a change as narrow as possible.
Loose build-time coupling minimizes how much unchanged code has to be rebuilt and retested as a consequence of a change.
And the decision to divide an application into services, each with its own deployment pipeline, is an architectural decision too.
So architecture is absolutely essential if you are going to benefit from coding agents.&lt;/p&gt;

&lt;h3 id=&quot;agents-need-guardrails&quot;&gt;Agents need guardrails&lt;/h3&gt;

&lt;p&gt;I have had success with GenAI when there are strong &lt;a href=&quot;/post/architecture/2026/03/09/genai-development-platform-part-1-development-guardrails.html&quot;&gt;guardrails&lt;/a&gt; in place:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Automated tests - one of the first guardrails.&lt;/li&gt;
  &lt;li&gt;The more boring, deterministic guardrails - pre-commit hooks that run code-quality checks (I use CodeScene) and vulnerability checks, to keep the AI from doing something stupid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise it writes code that it never uses.
On one project, 30% of the generated code was dead code - which admittedly happens with humans too, but this is meant to be world-changing technology.&lt;/p&gt;

&lt;p&gt;Michael Feathers defined legacy code as code without tests.
A tremendous amount of the code being written today does not have proper automated tests, so you let agents loose on it and who knows what bugs they are going to introduce.&lt;/p&gt;

&lt;h3 id=&quot;software-development-is-still-a-human-activity&quot;&gt;Software development is still a human activity&lt;/h3&gt;

&lt;p&gt;There is also a troubling trend: the idea that AI generates code so fast that we can’t review it, so we must find other ways to validate it.
Call me old-fashioned, but software development is still a human activity.
It is your code, with your name on it, that you are committing and are accountable for.
And I do not think coding agents are capable of the good design that human beings are.&lt;/p&gt;

&lt;h2 id=&quot;brownfield-is-not-greenfield&quot;&gt;Brownfield is not greenfield&lt;/h2&gt;

&lt;p&gt;Luca asked whether AI can really take a 20-year-old ERP and quickly move it to whatever architecture you want.&lt;/p&gt;

&lt;h3 id=&quot;genai-helps-you-understand-legacy-code&quot;&gt;GenAI helps you understand legacy code&lt;/h3&gt;

&lt;p&gt;GenAI is a useful tool for understanding a legacy codebase, though you have to take steps to ensure that it does not hallucinate.
I have had it produce architecture documentation that was quite imaginary: half of it was real, and half was how it thought the system should work, even though it does not.&lt;/p&gt;

&lt;h3 id=&quot;modernization-is-more-than-generating-code&quot;&gt;Modernization is more than generating code&lt;/h3&gt;

&lt;p&gt;GenAI can also generate code, but it needs the guardrails, the tests, and the specification in order to generate the right code.
And modernizing an application involves a lot more than generating code.
Quite often you are having to reverse-engineer the requirements.
You are also having to design how the new application should work, applying modern techniques such as domain-driven design, good boundaries, and modularity.
And as you build it, you are having to learn the new technology you are building it with.
There is all this learning going on, and you do it incrementally.
That is why I much prefer the &lt;a href=&quot;/post/refactoring/2023/06/21/strangler-fig-application-pattern-incremental-modernization-to-services.md.html&quot;&gt;Strangler Fig pattern&lt;/a&gt;, where you migrate iteratively, over a big bang approach where you get no validation until you are done.&lt;/p&gt;

&lt;h3 id=&quot;nextjs-is-not-your-legacy-system&quot;&gt;Next.js is not your legacy system&lt;/h3&gt;

&lt;p&gt;There are some well-known examples of rewrites.
Someone rewrote Next.js in a week.
But Next.js had a very exhaustive test suite, was well documented, and the web is full of content about it.
That is not your legacy system.&lt;/p&gt;

&lt;p&gt;I see the same thing on one of my side projects, a language interpreter.
I can tell Claude Code “implement language feature X” and it goes off and implements it perfectly, because that language feature is incredibly well documented.
Whereas when I am doing normal enterprise-style development, it muddles around and it is a whole lot harder to figure out what needs to be done.
A legacy application is a million times worse: a 30-year-old application that might even predate the web.&lt;/p&gt;

&lt;h3 id=&quot;convince-me&quot;&gt;Convince me&lt;/h3&gt;

&lt;p&gt;I am willing to be convinced that there is finally some magic technology where you press a button and, hey presto, you get a modern, well-architected application within a week.
But people have to demonstrate that it works and that it is not just a big-tech smoke-and-mirrors demo, like a lot of stuff is.
Convince me.&lt;/p&gt;

&lt;h2 id=&quot;get-the-fundamentals-right&quot;&gt;Get the fundamentals right&lt;/h2&gt;

&lt;p&gt;Luca asked me what advice I would give architects going into 2026.
My answer has not changed much, because the problems have not changed much either.&lt;/p&gt;

&lt;h3 id=&quot;a-50-year-old-idea-that-still-has-not-caught-on&quot;&gt;A 50-year-old idea that still has not caught on&lt;/h3&gt;

&lt;p&gt;Most enterprises are struggling with the fundamentals.
Modularity and loose coupling date back to Parnas in 1972.
That is a 50-year-old concept, and it still has not caught on.
A change should ideally happen within one team, but a lot of enterprises have built software that requires a massive number of meetings to coordinate and align in order to roll out a really simple change.
There is all this new shiny stuff, but software development would be way better if people got the hang of the fundamentals.
And maybe automated testing will catch on too.&lt;/p&gt;

&lt;h3 id=&quot;the-job-of-an-architect-is-to-solve-problems&quot;&gt;The job of an architect is to solve problems&lt;/h3&gt;

&lt;p&gt;My advice is the same as always.
The job of an architect is to solve problems, and you need a disciplined way of doing that:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;define the problem,&lt;/li&gt;
  &lt;li&gt;come up with some success criteria,&lt;/li&gt;
  &lt;li&gt;brainstorm candidate solutions,&lt;/li&gt;
  &lt;li&gt;evaluate them against the criteria, and&lt;/li&gt;
  &lt;li&gt;pick the best approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Above all, actually think things through.&lt;/p&gt;

&lt;h3 id=&quot;demand-proof&quot;&gt;Demand proof&lt;/h3&gt;

&lt;p&gt;Have a healthy skepticism.
You do not want to be super negative, but you should demand proof: &lt;a href=&quot;/post/architecture/2026/01/19/psa-no-silver-bullets.html&quot;&gt;extraordinary claims require extraordinary evidence&lt;/a&gt;.
Just because some random person on social media says “this worked for me” does not mean it is going to work for you.
My mindset is heavily patterns oriented: proven solutions to problems in a given context.
Your context is not the same as the random person on X’s context, and you have to carefully think it through.&lt;/p&gt;

&lt;h2 id=&quot;what-else-we-got-into&quot;&gt;What else we got into&lt;/h2&gt;

&lt;p&gt;On the episode, Luca and I also discuss:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;why code is not free - human attention is still a limited resource - and why “three commits before breakfast” leads to burnout: a relentless pair programming partner who won’t stop, and you can’t stop either&lt;/li&gt;
  &lt;li&gt;agents for everything: the same overuse we saw with microservices, and how to decide which sub-problems actually need a non-deterministic agent&lt;/li&gt;
  &lt;li&gt;“SaaS is dead”, and why big tech is so desperate to make GenAI look revolutionary&lt;/li&gt;
  &lt;li&gt;why Luca thinks we are all juniors again, and what it feels like to use GenAI without a conceptual framework&lt;/li&gt;
  &lt;li&gt;whether GenAI is undermining the business model of open source, and whether it is hard to justify hiring junior developers&lt;/li&gt;
  &lt;li&gt;whether this can even be a profitable industry once you get past Nvidia, the cloud providers, and the consulting companies&lt;/li&gt;
  &lt;li&gt;whether spec-driven development is a return to waterfall&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can watch the whole conversation on the &lt;a href=&quot;https://www.youtube.com/watch?v=0FF5r8ebuZQ&quot;&gt;Dear Architects podcast&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Wed, 02 Sep 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/09/02/dear-architects-microservices-and-ai.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/09/02/dear-architects-microservices-and-ai.html</guid>
        </item>
        
        <item>
            <title>Architecting for uncertainty - part 1: thriving in a VUCA world requires frequent deployment</title>
            <description>&lt;p&gt;This is the first in a series of articles based on my talk &lt;em&gt;Architecting for Fast Flow: Thriving Amid Uncertainty&lt;/em&gt;.
In this series, I define the term &lt;em&gt;fast flow&lt;/em&gt; and explain why it has become an essential software delivery capability.
I discuss how fast flow emerges from a &lt;em&gt;socio-technical architecture&lt;/em&gt;, which consists of a software architecture and its development organization.
Finally, I describe how to design and evolve a socio-technical architecture in order to sustain fast flow.&lt;/p&gt;

&lt;p&gt;The articles in the series are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-1-thriving-in-a-vuca-world-requires-frequent-deployment/&quot;&gt;Thriving in a VUCA world requires frequent deployment&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-2-fast-flow-defined/&quot;&gt;Fast flow defined&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-3-genai-as-an-amplifier&quot;&gt;GenAI as an amplifier&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This first article is the motivation for the rest of the series: today’s business environment is turbulent, and businesses must rapidly adapt their products and services.
And since modern businesses are powered by software, their ability to adapt depends on how quickly they can change their software.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/architecting-for-an-uncertain-world/part-1/our-planet-is-vuca.png&quot; alt=&quot;Our planet is VUCA&quot; style=&quot;width: 100%; margin-right: 1.5em; margin-bottom: 1em;&quot; /&gt;&lt;/p&gt;
&lt;p style=&quot;font-size: 0.75em; font-weight: bold;&quot;&gt;Earth image: NASA / Artemis II&lt;/p&gt;

&lt;p&gt;Earth is quite beautiful.
But for its inhabitants it’s increasingly turbulent.
Or, to use the language of management theory, it’s VUCA: volatile, uncertain, complex and ambiguous.
The result is a world that is harder to predict, understand and navigate.
In this article, I describe the impact of VUCA on businesses and explain why thriving in such a world requires frequent experimentation - and hence frequent deployment.&lt;/p&gt;

&lt;p style=&quot;font-size: 1.25em; font-weight: bold;&quot;&gt;&lt;a href=&quot;https://premium.microservices.io/architecting-for-uncertainty-part-1-thriving-in-a-vuca-world-requires-frequent-deployment/&quot;&gt;Read more (This post is for paying subscribers only)&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Tue, 25 Aug 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/08/25/architecting-for-uncertainty-part-1.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/08/25/architecting-for-uncertainty-part-1.html</guid>
        </item>
        
        <item>
            <title>Speed limits, GenAI coding agents and Autobahns - part 2: raising the safe speed</title>
            <description>&lt;p&gt;&lt;a href=&quot;/post/architecture/2026/08/04/speed-limits-genai-coding-agents-autobahns-part-1.html&quot;&gt;Part 1&lt;/a&gt; developed a driving metaphor for GenAI-based development: you are the driver, the coding agent and its harness are the car, and your code and delivery system are the road.
And in the same way that the road determines how fast you can drive, the characteristics of the code and the socio-technical architecture around it determine how fast you should let the agent generate code, and how much cognitive debt, if any, is acceptable.&lt;/p&gt;

&lt;p&gt;Part 1 also covered the first of the three ways of working with a coding agent: understand the road - what condition the code and the socio-technical architecture around it are in - which tells you the safe speed.
This article describes two ways to raise the speed limit.
First, you can improve the road: the architecture, the tests, the guardrails and the deployment pipeline.
Second, you can improve the car: the model, the prompts, the tools and the workflow that make up the harness.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/genai/raising-the-safe-speed.png&quot; class=&quot;img-responsive&quot; alt=&quot;Improving the road - better architecture, faster automated tests, guardrails, and a better deployment pipeline - and improving the car - a better foundation model, better prompts, better tools and harness, and a better workflow - both lead to greater confidence that changes are correct and safe to ship, which in turn permits a higher safe speed.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Both of these improvements work the same way: they improve the conditions, increasing confidence that the agent’s output will be correct, and confidence is what sets the safe speed.
Let’s look at each improvement in turn, starting with the road.&lt;/p&gt;

&lt;h2 id=&quot;improve-the-road-make-a-stretch-safe-at-higher-speed&quot;&gt;Improve the road: make a stretch safe at higher speed&lt;/h2&gt;

&lt;p&gt;Unlike a driver, you aren’t stuck with the road as it is.
You can improve it: you can decouple the code, tighten the guardrails, limit the damage when something does get through, and speed up the &lt;a href=&quot;/post/architecture/2024/02/27/microservices-rules-2-fast-deployment-pipeline-premium.html&quot;&gt;deployment pipeline&lt;/a&gt;.
What’s more, road improvements are persistent: decouple a module once, and every future change to it is easier.&lt;/p&gt;

&lt;p&gt;Let’s look at each way to improve the road in turn.&lt;/p&gt;

&lt;h3 id=&quot;the-road-surface-loose-design-time-coupling&quot;&gt;The road surface: loose design-time coupling&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;/post/architecture/2025/03/10/premium-microservices-rules-7-loosely-design-time-coupled-services-part-1.html&quot;&gt;Loose design-time coupling&lt;/a&gt; is the road surface.
It localizes changes so that both people and agents can work faster, and the agent uses fewer tokens and less context.
A tightly coupled - 25 mph - section becomes a faster stretch of road once it’s decoupled.&lt;/p&gt;

&lt;h3 id=&quot;guardrails-automated-tests-and-checks&quot;&gt;Guardrails: automated tests and checks&lt;/h3&gt;

&lt;p&gt;Some improvements catch mistakes and prevent a bad change before it leaves the road: fast automated local tests, type checking, linters, CI, and human review.
The more reliably a mistake is caught early, the faster you can safely generate code.&lt;/p&gt;

&lt;p&gt;A check that depends on somebody remembering to run it isn’t really a guardrail, so automate it.
Pre-commit checks, Git hooks, CI jobs and automated pull request review can gate the change at different points, providing defense in depth.&lt;/p&gt;

&lt;h3 id=&quot;run-off-areas-canary-deployments-and-fast-rollback&quot;&gt;Run-off areas: canary deployments and fast rollback&lt;/h3&gt;

&lt;p&gt;A run-off area limits the damage when a mistake does get through.
The software equivalents are &lt;a href=&quot;/post/architecture/2024/10/06/premium-smaller-safe-reversible-steps-part-3-canary-deployments.html&quot;&gt;canary deployments&lt;/a&gt;, fast automated rollback and &lt;a href=&quot;/post/architecture/2025/02/17/premium-microservices-rules-10-smaller-user-stories.html&quot;&gt;smaller batches&lt;/a&gt;.
The cheaper a mistake is to recover from, the less it costs to be wrong, and the faster you can safely go.&lt;/p&gt;

&lt;h3 id=&quot;extra-lanes-microservices-with-their-own-deployment-pipelines&quot;&gt;Extra lanes: microservices with their own deployment pipelines&lt;/h3&gt;

&lt;p&gt;Just as a road becomes congested when traffic exceeds its capacity, a deployment pipeline becomes a bottleneck when changes arrive faster than it can deploy them.
A handful of developers running agents can generate more commits in a day than a much larger team once did.
Joe Magerramov’s &lt;a href=&quot;https://lnkd.in/p/euznKdfC&quot;&gt;“The Valley of Calm”&lt;/a&gt; describes how at around a hundred commits a day, a pipeline “stops behaving like a pipeline and starts behaving like a traffic jam.”
Because batches are cumulative, a defect rejects the whole batch, and the commits that arrive while you fix it make the next batch bigger and more likely to fail again.
His simulation shows that beyond a certain point teams end up in a &lt;strong&gt;plateau of misery&lt;/strong&gt; where almost nothing ships.&lt;/p&gt;

&lt;p&gt;The remedy for road congestion is more lanes.
Similarly, as I describe in &lt;a href=&quot;/post/architecture/2026/02/08/architecting-for-genai-based-software-delivery.html&quot;&gt;GenAI-based software delivery needs a fast flow architecture&lt;/a&gt;, the solution for an overloaded deployment pipeline is a &lt;a href=&quot;/post/architecture/2024/08/27/architecting-microservices-for-fast-flow.html&quot;&gt;microservice architecture&lt;/a&gt;.
Each service &lt;a href=&quot;/post/architecture/2025/05/20/premium-microservices-rules-8-design-independently-deployable-services.html&quot;&gt;has its own deployment pipeline&lt;/a&gt;, which handles fewer commits from a single team and verifies a smaller code base.
Collectively, these deployment pipelines can process many more commits than a single shared pipeline.&lt;/p&gt;

&lt;h2 id=&quot;improve-the-car-strengthen-the-agent-and-its-harness&quot;&gt;Improve the car: strengthen the agent and its harness&lt;/h2&gt;

&lt;p&gt;Now let’s consider the car.
Besides its speed, a real car has safety features: high-performance brakes, collision avoidance, seatbelts, and airbags.
A coding agent is similar.
The raw model sets the top speed, and the harness is what makes it safe to use.&lt;/p&gt;

&lt;p&gt;Structurally, a harness is the &lt;a href=&quot;/post/architecture/2026/05/19/genai-development-platform-part-4-coding-agent-sandwich.html&quot;&gt;coding agent sandwich&lt;/a&gt;: plain old code orchestrating on top, the model in the middle, and deterministic tools underneath.
My own &lt;a href=&quot;/post/architecture/2026/03/17/overview-i2code-implement-agent-orchestrator.html&quot;&gt;implement agent orchestrator&lt;/a&gt; is built that way.&lt;/p&gt;

&lt;p&gt;The harness both prevents mistakes and catches them.
As Birgitta Böckeler writes in &lt;a href=&quot;https://martinfowler.com/articles/harness-engineering.html&quot;&gt;Harness Engineering&lt;/a&gt;, a harness consists of guides that shape what the agent does before it acts, and sensors that provide feedback so it can correct course afterward.&lt;/p&gt;

&lt;h3 id=&quot;guides-shaping-what-the-agent-does-before-it-acts&quot;&gt;Guides: shaping what the agent does before it acts&lt;/h3&gt;

&lt;p&gt;Guides tell the agent how to work: coding conventions, architecture documents, skills and how-to guides, and the deterministic tooling underneath - e.g. language servers, CLIs, automated refactoring tools - that makes the right thing the easy thing.
Keep them in good order so that the agent has what it needs.
The better the guides, the more often the agent gets it right the first time, so the less you have to slow down to check its work.&lt;/p&gt;

&lt;h3 id=&quot;sensors-providing-feedback-to-the-agent&quot;&gt;Sensors: providing feedback to the agent&lt;/h3&gt;

&lt;p&gt;Sensors provide feedback enabling the agent to course-correct: linting, static type checking, dead code detection, test coverage verification, and code health analysis such as &lt;a href=&quot;https://codescene.io/&quot;&gt;CodeScene&lt;/a&gt;.
I described some of these in &lt;a href=&quot;/post/architecture/2026/03/09/genai-development-platform-part-1-development-guardrails.html&quot;&gt;a GenAI-based development platform&lt;/a&gt;.
Those all run against the code, but a sensor can also report from the running system: &lt;a href=&quot;/post/architecture/2025/08/02/premium-microservices-rules-9-observable-services.html&quot;&gt;observability&lt;/a&gt; data tells the agent what a change actually did, not just whether the code looks correct.&lt;/p&gt;

&lt;p&gt;A check that is a sensor can also be a guardrail, and vice versa.
When a check runs in the pipeline, it’s a guardrail that gates the change.
And when it runs alongside the agent, it is a sensor that provides feedback so that the agent fixes the problem itself.
The better the sensors, the more mistakes the agent catches and fixes before you ever see them, so the more confident you can be in its output, and the faster you can let it go.&lt;/p&gt;

&lt;p&gt;In the same way that a better car lets you go faster on most roads without removing the road’s limits, a better harness lets you go faster with a coding agent without removing the limits imposed by the codebase and the team.
That’s why it’s important to improve both the road and the car.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;The safe speed isn’t fixed.
Improving the road - the architecture, the tests, the guardrails and the deployment pipeline - and improving the car - the harness around the model - both raise it.
They increase confidence that the agent’s output will be correct, and confidence is what sets the safe speed.&lt;/p&gt;

&lt;p&gt;That’s what &lt;a href=&quot;/post/architecture/2024/04/10/success-triangle-is-about-fast-flow.html&quot;&gt;fast flow&lt;/a&gt; means in practice: moving fast without breaking things.
Set the speed limit &lt;a href=&quot;/post/architecture/2024/08/04/microservices-rules-5-deliberative-decisions.html&quot;&gt;deliberately&lt;/a&gt;, then do the work to raise it.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Sat, 22 Aug 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/08/22/speed-limits-genai-coding-agents-autobahns-part-2.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/08/22/speed-limits-genai-coding-agents-autobahns-part-2.html</guid>
        </item>
        
        <item>
            <title>Microservices Platforms: When Team Topologies Meets Microservices Patterns - QCONSF 2025 video</title>
            <description>&lt;p&gt;The &lt;a href=&quot;https://www.infoq.com/presentations/microservices-platform-team-topology/&quot;&gt;video and transcript&lt;/a&gt; of my QCON San Francisco 2025 talk on microservices platforms - the basis for the &lt;a href=&quot;https://premium.microservices.io/microservices-platforms-when-team-topologies-meets-microservices-patterns-part-1/&quot;&gt;article series&lt;/a&gt; - is now available.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.infoq.com/presentations/microservices-platform-team-topology/&quot; target=&quot;_blank&quot;&gt;
&lt;img src=&quot;/i/posts/qconsf-microservices-platforms-video.png&quot; alt=&quot;screenshot showing my talks title slide&quot; class=&quot;img-responsive&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;abstract&quot;&gt;Abstract&lt;/h2&gt;

&lt;p&gt;When many teams work on a large, complex application, the microservice architecture potentially enables them to work independently and deliver a continuous stream of changes. Yet, there’s a risk that those teams become burdened by needing to apply numerous patterns from the microservice architecture pattern language. Instead of writing domain logic, they must spend their time writing plumbing code and dealing with infrastructure. This overhead ultimately slows feature delivery, undermining the primary goals of adopting a microservice architecture.&lt;/p&gt;

&lt;p&gt;In this talk, I explain how to solve this problem by applying the Team Topologies’ concept of a platform, which reduces the workload for teams and enables them to focus on domain logic. You will learn how this platform approach directly tackles the most common challenges of implementing the microservice pattern language. I will then describe several kinds of platforms and the key capabilities they provide to free development teams from this implementation burden.&lt;/p&gt;

&lt;h2 id=&quot;articles&quot;&gt;Articles&lt;/h2&gt;

&lt;p&gt;Here’s the series of articles that I wrote based on this talk:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-when-team-topologies-meets-microservices-patterns-part-1/&quot;&gt;Microservices Platforms - part 1: Overview&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-2-service-foundation-platform/&quot;&gt;Microservices Platforms - part 2: Service foundation platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-3-security-platform/&quot;&gt;Microservices Platforms - part 3: Security platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-4-infrastructure-services-platform/&quot;&gt;Microservices Platforms - part 4: Infrastructure services platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-5-observability-platform/&quot;&gt;Microservices Platforms - part 5: Observability platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-6-build-platform/&quot;&gt;Microservices Platforms - part 6: Build platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-7-deployment-platform/&quot;&gt;Microservices Platforms - part 7: Deployment platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://premium.microservices.io/microservices-platforms-part-8-getting-started-with-platforms/&quot;&gt;Microservices Platforms - part 8: Getting started with platforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;slides&quot;&gt;Slides&lt;/h2&gt;

&lt;p&gt;See the &lt;a href=&quot;/post/architecture/2025/11/23/qconsf-2025-microservices-platforms.html&quot;&gt;slides&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;video-and-transcript&quot;&gt;Video and transcript&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.infoq.com/presentations/microservices-platform-team-topology/&quot; target=&quot;_blank&quot;&gt;
&lt;img src=&quot;/i/posts/qconsf-microservices-platforms-video.png&quot; alt=&quot;screenshot showing my talks title slide&quot; class=&quot;img-responsive&quot; /&gt;
&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Wed, 05 Aug 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/08/05/microservices-platforms-qconsf-2025-video.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/08/05/microservices-platforms-qconsf-2025-video.html</guid>
        </item>
        
        <item>
            <title>Speed limits, GenAI coding agents and Autobahns - part 1: how fast should you go?</title>
            <description>&lt;p&gt;GenAI coding agents enable a team to generate code rapidly.
However, each team member is powered by a 20 W organic processing substrate, and there’s a limit to the rate at which they can read and understand code.
As a result, the team cannot keep up, creating a serious problem: cognitive debt.
Code gets committed and deployed that nobody understands.
The gap between what the system does and what the team knows it does keeps widening.&lt;/p&gt;

&lt;p&gt;But it does not have to be this way.
Just because an agent can generate code faster than a team can understand it doesn’t mean a team should let it.
The question every team should be asking is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;How fast should we let the agent go on our code?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My sense, however, is that not enough teams are asking this question.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/genai/code-velocity-speedometer.jpg&quot; class=&quot;img-responsive&quot; alt=&quot;A speedometer whose dial is divided into a green zone for generated boilerplate, adapters, tests and config, an amber zone for supporting code and business logic, and a red zone for core domain, complex logic and novel design&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This article is the first of two about how a team can answer this question.
In this part, I’ll explain why you can slow down and avoid creating cognitive debt, and then introduce the metaphor I use in both articles: a fast car.
I give the three reasons I don’t drive my car at its top speed, and what each of them corresponds to in software.
I then deal with the two obvious counter-examples: the Autobahn, which has stretches with no posted speed limit, and Formula 1, where maximum speed is the entire point.
Finally, I describe how to work out the speed that’s safe for your code.
&lt;a href=&quot;/post/architecture/2026/08/22/speed-limits-genai-coding-agents-autobahns-part-2.html&quot;&gt;Part 2&lt;/a&gt; covers how to increase the speed limit.
Let’s start by looking at why we have a choice about how fast to let a coding agent go.&lt;/p&gt;

&lt;h2 id=&quot;you-choose-how-fast-the-agent-goes&quot;&gt;You choose how fast the agent goes&lt;/h2&gt;

&lt;p&gt;Rapid code generation that exceeds human understanding is usually presented as inevitable.
And dealing with it is an active research topic, with AI-assisted code review and better tooling as examples.
But usually the generation rate is taken as given and work focuses on helping teams keep up.&lt;/p&gt;

&lt;p&gt;In fact, the rate at which an agent &lt;em&gt;can&lt;/em&gt; generate code isn’t the rate a team has to accept.
Teams have control over the rate of code generation and the amount of cognitive debt they create.
To understand why, let’s look at the metaphor of a fast car.&lt;/p&gt;

&lt;h2 id=&quot;why-i-dont-drive-at-my-cars-top-speed&quot;&gt;Why I don’t drive at my car’s top speed&lt;/h2&gt;

&lt;p&gt;This metaphor consists of the following elements:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;You&lt;/strong&gt; are the driver.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The car&lt;/strong&gt; is the coding agent, together with the harness around it.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The road&lt;/strong&gt; is the code, and the socio-technical architecture.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Speed&lt;/strong&gt; is the rate at which code is generated and deployed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like a coding agent, my car is fast.
According to the specs, it has a top speed of around 150 mph.
But I don’t drive that fast.
Let’s look at the three reasons why.&lt;/p&gt;

&lt;h3 id=&quot;on-many-roads-it-isnt-safe&quot;&gt;On many roads it isn’t safe&lt;/h3&gt;

&lt;p&gt;First, it’s often unsafe.
I could injure or kill myself, or someone else.
Rapidly generating code when understanding is required - core logic, novel design, etc. - means deploying code the team can’t reason about.
That’s cognitive debt.&lt;/p&gt;

&lt;h3 id=&quot;speed-limits&quot;&gt;Speed limits&lt;/h3&gt;

&lt;p&gt;Second, traffic laws require me to observe the speed limit, typically 25 to 65 mph.
I could ignore them, but I’d risk a ticket.
With code there’s no highway patrol, but a team can choose its own limits: for example, that a team member must understand a change before it merges.&lt;/p&gt;

&lt;h3 id=&quot;but-the-road-ahead-is-jammed&quot;&gt;But the road ahead is jammed&lt;/h3&gt;

&lt;p&gt;Third, there’s congestion.
The car’s top speed is irrelevant when the road is full.
For instance, the average traffic speed in many major cities is under 15 mph!&lt;/p&gt;

&lt;p&gt;As I describe in part 2, without the right architecture, a coding agent can generate more changes than the &lt;a href=&quot;/post/architecture/2024/02/27/microservices-rules-2-fast-deployment-pipeline-premium.html&quot;&gt;deployment pipeline&lt;/a&gt; can process.
If the deployment pipeline is overwhelmed, faster code generation produces fewer successful deployments, not more.&lt;/p&gt;

&lt;h2 id=&quot;but-what-about-the-autobahn&quot;&gt;But what about the Autobahn?&lt;/h2&gt;

&lt;p&gt;There are, of course, places where you can drive much faster.
The Autobahn has stretches with no posted speed limit.
But drivers are legally required to adjust their speed to the traffic, weather, visibility and road conditions.
And there’s an advisory 130 km/h (81 mph) that can count against them in a liability claim if they crash above it.&lt;/p&gt;

&lt;p&gt;The Autobahn is a safe road system for the following reasons:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;The permitted speed is matched to the conditions.&lt;/strong&gt; Permanent limits where the road demands them, variable limits where conditions change, and unrestricted stretches only where the design allows.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;The road is engineered to support higher speeds safely.&lt;/strong&gt; The surface, the shoulders, the banking, and the guardrails.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Vehicles and drivers are held to a high standard.&lt;/strong&gt; Demanding licensing, and mandatory periodic inspection of every car.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The unrestricted stretches are unrestricted &lt;em&gt;because the conditions there allow it&lt;/em&gt;, and a limit appears on the same road on the same day when the conditions change.
It’s a great example of matching the speed limit to the conditions.&lt;/p&gt;

&lt;h2 id=&quot;and-then-theres-f1&quot;&gt;And then there’s F1&lt;/h2&gt;

&lt;p&gt;Formula 1 might appear to be another example of how to go fast.
The custom-designed cars are far faster and are driven at their limit.
But the drivers are professionals with teams monitoring telemetry.
Also, there are only two dozen circuits.
The drivers know each one intimately and rehearse for hours on simulators before the race.
Part of what makes the speed safe is that nothing about the track is a surprise.&lt;/p&gt;

&lt;p&gt;But software development involves novelty.
There’s no equivalent of simulator time or practice sessions before the race.
In fact, if an application were sufficiently standardized, you’d be better off &lt;a href=&quot;/post/architecture/2024/04/16/doing-more-with-less-part-2-build-vs-buy-using-DDD.html&quot;&gt;buying it than building it&lt;/a&gt;.
So F1 isn’t a suitable metaphor for software development.&lt;/p&gt;

&lt;h2 id=&quot;understand-the-road-improve-the-road-improve-the-car&quot;&gt;Understand the road, improve the road, improve the car&lt;/h2&gt;

&lt;p&gt;Now that we’ve explored the metaphor of a fast car, let’s return to the question of how fast to let a coding agent go.
Three aspects of the Autobahn’s safety map onto three ways to work with a coding agent:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Understand the road&lt;/strong&gt; - determine the coding agent’s safe speed for each module of your application&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improve the road&lt;/strong&gt; - build the infrastructure that enables the coding agent to work safely at higher speed&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Improve the car&lt;/strong&gt; - improve the agent and its harness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part 2 describes how to improve the road and the car.
The rest of this article looks at how to understand the road.&lt;/p&gt;

&lt;h2 id=&quot;understand-the-road-how-fast-can-you-drive-it&quot;&gt;Understand the road: how fast can you drive it?&lt;/h2&gt;

&lt;p&gt;Understanding the road tells you what speed is safe.
It’s how you recognize a school zone or a winding mountain road and slow down before you get there.
Similarly, the characteristics of the software determine the coding agent’s safe speed.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/genai/conditions-determine-agent-speed.png&quot; class=&quot;img-responsive&quot; alt=&quot;Two parallel columns. On the left, driving: road geometry and design, traffic conditions, weather and visibility, and driver and vehicle capability feed into confidence that higher speed is safe, which in turn determines the appropriate speed. On the right, AI-assisted coding: code characteristics, architecture and design-time coupling, fast feedback, and agent and harness quality feed into confidence that the change is correct, which determines the appropriate generation speed.&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here are some per-module characteristics that indicate the safe speed for a coding agent:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;DDD, core versus everything else.&lt;/strong&gt; The core domain is a 25 mph zone. Supporting and generic subdomains are usually open road, and &lt;a href=&quot;/post/architecture/2023/08/14/assemblage-overview-part-2-defining-subdomains.html&quot;&gt;defining those subdomains&lt;/a&gt; is what tells you which is which.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Hexagonal, domain versus adapters.&lt;/strong&gt; Adapters and glue code are usually open road. The domain model rarely is.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Coupling and testability.&lt;/strong&gt; &lt;a href=&quot;/post/architecture/2023/03/28/microservice-architecture-essentials-loose-coupling.html&quot;&gt;Loosely coupled&lt;/a&gt;, locally &lt;a href=&quot;/post/architecture/2025/07/09/premium-microservices-rules-8-testable-services.html&quot;&gt;testable&lt;/a&gt; code is open road, because changes stay contained and fast tests catch mistakes. Tightly coupled, hard-to-test code is a 25 mph zone regardless of anything else.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;/post/architecture/2026/03/01/using-genai-based-coding-agents-cynefin-complex-domain.html&quot;&gt;Cynefin&lt;/a&gt;: clear, complicated or complex.&lt;/strong&gt; Clear and complicated work - well-established patterns the model has seen many times - allows speed, because a known good practice applies. Complex work, where novel architecture has no precedent, does not: you have to probe and learn, so slow down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two more characteristics apply to the application as a whole rather than to individual modules:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Application scale and purpose.&lt;/strong&gt; A throwaway prototype differs from a long-lived production system, and low blast radius differs from high.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;GenAI capability.&lt;/strong&gt; How well represented is this work in the model’s training? A common stack allows speed. An obscure or bespoke one does not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Together, those characteristics determine how much confidence you can have that the agent’s output will be correct.
It is this confidence that sets the safe speed, as the diagram above illustrates.
Where confidence is high, let the agent go faster.
Where it’s low, slow down and review.&lt;/p&gt;

&lt;p&gt;And a road trip is rarely all one kind of road.
It might start on suburban streets, join the freeway for two hundred miles, climb a winding mountain pass, and finish in a school zone.
The safe speed changes several times along the way, and a good driver adjusts each time.&lt;/p&gt;

&lt;p&gt;An application is the same.
The per-module characteristics vary from one module to the next, and so does the confidence they give you.
The safe speed is therefore mostly a property of the module you’re changing, rather than a single number for the whole application.
Implementing a single feature can involve modules with very different safe speeds, so expect to slow down and speed up as you go.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;The road you’re on determines how fast you can drive: an Autobahn, a winding mountain road, a school zone, etc.
Code is the same.
The characteristics of the codebase and the team determine how fast you should let the agent generate code, and how much cognitive debt, if any, is acceptable.
And just as a road trip covers several kinds of road, an application contains several kinds of code, each with its own safe speed.&lt;/p&gt;

&lt;p&gt;But unlike a driver, who takes the road and the car as they are, you shape both.
&lt;a href=&quot;/post/architecture/2026/08/22/speed-limits-genai-coding-agents-autobahns-part-2.html&quot;&gt;Part 2&lt;/a&gt; covers how to raise the safe speed: improve the road, and improve the car.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Tue, 04 Aug 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/08/04/speed-limits-genai-coding-agents-autobahns-part-1.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/08/04/speed-limits-genai-coding-agents-autobahns-part-1.html</guid>
        </item>
        
        <item>
            <title>Q&amp;A about my Explore DDD designing microservices workshop</title>
            <description>&lt;p&gt;I’m teaching an updated version of my workshop - &lt;a href=&quot;https://chrisrichardson.net/training-microservice-architecture-definition-workshop.html&quot;&gt;Designing microservices: responsibilities, APIs and collaborations&lt;/a&gt; - at the excellent &lt;a href=&quot;https://exploreddd.com/&quot;&gt;Explore DDD conference&lt;/a&gt; that’s happening this September in Denver.&lt;/p&gt;

&lt;p&gt;Here’s a Q&amp;amp;A that explores why this workshop is especially relevant in the age of GenAI, the common mistakes teams make when designing and developing microservices, and how the workshop equips attendees with a practical, repeatable approach to building microservice architectures that enable fast, sustainable software delivery.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/posts/designing-microservices-responsibilities-apis-an_richardson_1154435_banner.jpeg&quot; alt=&quot;Designing microservices: responsibilities, APIs and collaborations workshop banner&quot; class=&quot;img-responsive&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;whats-changed-most-about-microservices-design-in-recent-years&quot;&gt;What’s changed most about microservices design in recent years?&lt;/h2&gt;

&lt;p&gt;The recent surge in adoption of GenAI coding agents has strengthened the case for the microservice architecture. That’s because coding agents accelerate the &lt;a href=&quot;/post/architecture/2026/02/08/architecting-for-genai-based-software-delivery.html&quot;&gt;flow of changes&lt;/a&gt;, which in turn demands an architecture that can support that increased flow rate. As a result, organizations will outgrow the monolithic architecture much sooner. What’s more, a service’s smaller codebase improves the effectiveness of coding agents.&lt;/p&gt;

&lt;h2 id=&quot;and-whats-stayed-stubbornly-the-same&quot;&gt;And what’s stayed stubbornly the same?&lt;/h2&gt;

&lt;p&gt;Designing a microservice architecture is still high stakes and challenging. A bad architecture will slow down delivery and threaten the success of the business. Yet at the same time, defining an architecture requires deep thinking to navigate &lt;a href=&quot;/post/architecture/2023/03/26/dark-energy-dark-matter-force-descriptions.html&quot;&gt;numerous tradeoffs&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;where-does-ai-genuinely-help-in-designing-services-and-where-does-it-often-confidently-steer-someone-wrong&quot;&gt;Where does AI genuinely help in designing services, and where does it often confidently steer someone wrong?&lt;/h2&gt;

&lt;p&gt;GenAI is a helpful design assistant. It can extract domain models and APIs from requirements, suggest service decompositions, identify responsibilities, generate APIs, and rapidly explore alternatives.
However, despite appearances to the contrary, GenAI is a next-token predictor that understands nothing and cannot reason. It can define a superficially plausible yet deeply flawed architecture - for example, one that looks reasonable at a glance but bakes in tight coupling or the wrong service boundaries. Human architects are therefore still essential: they define the architectural guardrails that guide GenAI, and they exercise the judgment needed to make the architectural decisions.&lt;/p&gt;

&lt;h2 id=&quot;what-mistakes-do-teams-most-often-make-when-they-carve-a-system-into-microservices&quot;&gt;What mistake(s) do teams most often make when they carve a system into microservices?&lt;/h2&gt;

&lt;p&gt;I regularly encounter organizations that have built an excessively &lt;a href=&quot;/post/architecture/2023/09/21/about-service-granularity.html&quot;&gt;fine-grained service architecture&lt;/a&gt;, where each small team owns many services - e.g., one service per team member, or worse. Such architectures are difficult to understand and maintain, and there’s often tight design-time and runtime coupling. What’s more, instead of solving business problems, teams are forced to deal with &lt;a href=&quot;/post/patterns/2023/07/29/service-collaboration-patterns.html&quot;&gt;distributed systems issues such as complex coordination logic&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;what-does-a-day-in-this-workshop-actually-look-like-hands-on&quot;&gt;What does a day in this workshop actually look like, hands-on?&lt;/h2&gt;

&lt;p&gt;While there are lectures, the core of the workshop - and the most fun part - is the design exercises and the discussions. The day alternates between short lectures that introduce concepts and exercises that immediately put them into practice, with attendees working together in small groups to design a microservice architecture. After each design exercise, there are great discussions comparing the different groups’ designs, debating the tradeoffs, and discovering that there’s rarely one right answer.&lt;/p&gt;

&lt;h2 id=&quot;what-will-attendees-be-able-to-bring-back-to-their-teams-to-help-them-succeed-with-microservices&quot;&gt;What will attendees be able to bring back to their teams to help them succeed with microservices?&lt;/h2&gt;

&lt;p&gt;Attendees leave with several concrete takeaways:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;A deep understanding of key microservice architecture concepts, including &lt;a href=&quot;/post/architecture/2025/02/18/wrong-reasons-to-adopt-microservices.html&quot;&gt;when - and when not - to use microservices&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;/post/architecture/2023/02/09/assemblage-architecture-definition-process.html&quot;&gt;practical, repeatable approach for defining a microservice architecture&lt;/a&gt; with the appropriate level of granularity.&lt;/li&gt;
  &lt;li&gt;Techniques for designing services that are loosely coupled at both design-time and runtime.&lt;/li&gt;
  &lt;li&gt;How to apply the &lt;a href=&quot;/post/patterns/2023/07/29/service-collaboration-patterns.html&quot;&gt;service collaboration patterns&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;to-learn-more&quot;&gt;To learn more&lt;/h2&gt;

&lt;p&gt;To learn more about the workshop, and to register, visit the &lt;a href=&quot;https://mailchi.mp/exploreddd/2026-microservices-workshop&quot;&gt;Explore DDD conference website&lt;/a&gt;.
And, if you would like me to teach this workshop at your organization, please &lt;a href=&quot;https://chrisrichardson.net/contactus.html&quot;&gt;contact me&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Mon, 20 Jul 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/architecture/2026/07/20/qa-explore-ddd-microservices-workshop.html</link>
            <guid isPermaLink="true">http://microservices.io//post/architecture/2026/07/20/qa-explore-ddd-microservices-workshop.html</guid>
        </item>
        
        <item>
            <title>Migrating a Claude Code skill from WebFetch to the new CircleCI CLI: lessons learned about tool and API design</title>
            <description>&lt;p&gt;One of the Claude Code skills in my &lt;a href=&quot;https://github.com/humansintheloop-dev/humansintheloop-dev-workflow-and-tools/tree/master&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idea-to-code&lt;/code&gt; plugin&lt;/a&gt; is &lt;a href=&quot;https://github.com/humansintheloop-dev/humansintheloop-dev-workflow-and-tools/tree/master/claude-code-plugins/idea-to-code/skills/debugging-ci-failures&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;debugging-ci-failures&lt;/code&gt;&lt;/a&gt;.
It tells Claude Code how to watch and diagnose a failing CI build.
Specifically, it instructs Claude Code to do the following five steps:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Watch the build to completion&lt;/li&gt;
  &lt;li&gt;Identify the failing job&lt;/li&gt;
  &lt;li&gt;Fetch the failing step’s output&lt;/li&gt;
  &lt;li&gt;Download the test artifacts&lt;/li&gt;
  &lt;li&gt;Read the JUnit &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TEST-*.xml&lt;/code&gt; files to find the root cause&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The skill supports two CI systems: GitHub Actions (via the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh&lt;/code&gt; CLI) and CircleCI.
Until today, the CircleCI support used &lt;a href=&quot;https://docs.claude.com/en/docs/claude-code/tools&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;WebFetch&lt;/code&gt;&lt;/a&gt; — Claude Code’s built-in HTTP fetch tool — to access CircleCI’s REST API directly.
That worked, but it was clumsy.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/i/genai/idea-to-code/from-webfetch-to-circleci-cli.png&quot; alt=&quot;From WebFetch to CircleCI CLI&quot; style=&quot;max-width: 100%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This post is about how I used Claude Code to migrate the skill from WebFetch to the new CircleCI CLI.
The migration itself took a single one-line prompt — but that’s the least interesting part.
Along the way, Claude Code discovered CLI features I didn’t know existed and found a shortcut I hadn’t thought to ask for.
And by the end, a small skill update had turned into something more general: a lesson about how we should be designing tools for AI agents.
I’ll come back to that. First, the hack the new CLI let me delete.&lt;/p&gt;

&lt;h2 id=&quot;the-webfetch-approach-and-its-ugly-workaround&quot;&gt;The WebFetch approach and its ugly workaround&lt;/h2&gt;

&lt;p&gt;Compared to the GitHub &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh&lt;/code&gt; CLI, the existing CircleCI CLI was rather limited.
As a result, the skill had to use WebFetch to access CircleCI’s REST API directly, which was rather inconvenient.
Not only did Claude Code need to know the details of the REST API, but analyzing builds for private repositories required an API token, which was not something I wanted to expose to Claude Code.&lt;/p&gt;

&lt;p&gt;Also, the Claude Code WebFetch tool caches responses for about 15 minutes, which is a problem when you are invoking an API.
As a workaround, the skill had to append a cache-busting query parameter to every request:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;WebFetch: https://circleci.com/api/v1.1/project/github/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;?limit=1&amp;amp;branch=&amp;lt;branch&amp;gt;&amp;amp;_ts=1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;_ts=1&lt;/code&gt; at the end.
That’s not a CircleCI parameter — it’s a hack that forces WebFetch to treat each request as unique.&lt;/p&gt;

&lt;h2 id=&quot;the-new-circleci-cli&quot;&gt;The new CircleCI CLI&lt;/h2&gt;

&lt;p&gt;I was excited to read &lt;a href=&quot;https://www.linkedin.com/posts/robzuber_rebuilding-the-circleci-cli-from-scratch-share-7481370116476268545-iZuE&quot;&gt;Rob Zuber’s LinkedIn post&lt;/a&gt; announcing the new CircleCI CLI.
A quick glance showed that it was roughly comparable to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gh&lt;/code&gt; CLI for GitHub Actions — it handled authentication, and hid the details of the REST API.
What’s more, I discovered that changing the skill to use it was remarkably straightforward.&lt;/p&gt;

&lt;h2 id=&quot;the-prompt-that-started-the-migration&quot;&gt;The prompt that started the migration&lt;/h2&gt;

&lt;p&gt;After I reorganized the skill so that GitHub Actions and CircleCI each had their own section, my next prompt was a single line:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;For circleci some or all of the webfetch can be replaced by the new circleci CLI. For example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run list&lt;/code&gt; — you can try on eventuate-foundation/eventuate-common.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude Code walked the CLI’s help tree (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci --help&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run --help&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job --help&lt;/code&gt;, etc.), then ran commands against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;eventuate-foundation/eventuate-common&lt;/code&gt; to see the actual JSON shapes and behaviors before rewriting the skill.&lt;/p&gt;

&lt;p&gt;Take a look at the &lt;a href=&quot;https://gist.github.com/cer/ae9d749031a8445e0c014566bf5fb30b&quot;&gt;Claude Code session&lt;/a&gt; and the resulting &lt;a href=&quot;https://github.com/humansintheloop-dev/humansintheloop-dev-workflow-and-tools/commit/103980af9666b2fca94fdfcc0d3d9507c9dbf853&quot;&gt;commit&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;what-the-cli-replaces&quot;&gt;What the CLI replaces&lt;/h2&gt;

&lt;p&gt;The mapping turned out to be direct — the CLI has a purpose-built command for every step of the workflow:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Skill step&lt;/th&gt;
      &lt;th&gt;Old (WebFetch + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_ts&lt;/code&gt; hack)&lt;/th&gt;
      &lt;th&gt;New (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci&lt;/code&gt; CLI)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Watch a build&lt;/td&gt;
      &lt;td&gt;Poll &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/build/&amp;lt;n&amp;gt;?_ts=N&lt;/code&gt; in a loop&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run watch&lt;/code&gt; — blocks, exit code = outcome&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Find the latest run&lt;/td&gt;
      &lt;td&gt;WebFetch project endpoint&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run list --branch &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Get run + workflow + job status&lt;/td&gt;
      &lt;td&gt;WebFetch, parse JSON&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run get &amp;lt;run-id&amp;gt; --json&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Find the failed step&lt;/td&gt;
      &lt;td&gt;WebFetch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;?include=steps&lt;/code&gt;, scan&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job output list &amp;lt;job-id&amp;gt; --json --jq &apos;.steps[] \| select(.exit_code != 0)&apos;&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Fetch the failed step’s output&lt;/td&gt;
      &lt;td&gt;WebFetch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/output/&amp;lt;idx&amp;gt;/0&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job output get &amp;lt;job-id&amp;gt; --step-num N&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;List artifacts&lt;/td&gt;
      &lt;td&gt;WebFetch &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/artifacts&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci artifact &amp;lt;job-id&amp;gt; --json&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Download artifacts&lt;/td&gt;
      &lt;td&gt;WebFetch each URL individually&lt;/td&gt;
      &lt;td&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci artifact &amp;lt;job-id&amp;gt; --output test-reports&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Two of these are worth highlighting.&lt;/p&gt;

&lt;h3 id=&quot;circleci-run-watch&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run watch&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run watch&lt;/code&gt; replaces an entire polling loop with a blocking call whose exit code encodes the outcome:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0&lt;/code&gt; — all workflows succeeded&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt; — one or more workflows failed&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;6&lt;/code&gt; — cancelled&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;8&lt;/code&gt; — timed out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a much better contract for an agent than “keep polling until &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;status&lt;/code&gt; is no longer &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;running&lt;/code&gt;, then check &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;outcome&lt;/code&gt;.”&lt;/p&gt;

&lt;h3 id=&quot;circleci-job-output-get---condensed&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job output get --condensed&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Claude discovered that the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job output get&lt;/code&gt; command has a rather useful flag: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--condensed&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use –condensed to fetch a filtered version of stdout only, with noisy or repetitive lines removed server-side. This produces a much smaller payload suitable for feeding to an AI tool. When –condensed is set, output is always rendered through the plain-text renderer regardless of the terminal or –strip-ansi.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CircleCI has apparently added a first-class “give me the interesting lines, meant to be pasted into an LLM” mode to their CLI.&lt;/p&gt;

&lt;h2 id=&quot;an-unexpected-shortcut&quot;&gt;An unexpected shortcut&lt;/h2&gt;

&lt;p&gt;While Claude Code studied the CLI’s commands, it found another one that isn’t strictly a WebFetch replacement — it’s an entirely new step Claude can try &lt;em&gt;before&lt;/em&gt; downloading anything:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;circleci testresult list &amp;lt;job-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, this command shows a job’s failed tests.
It enables Claude Code to quickly identify the failing test without downloading any artifacts.
The skill now tries &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;testresult list&lt;/code&gt; first and falls back to downloading &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TEST-*.xml&lt;/code&gt; files only if that isn’t enough.&lt;/p&gt;

&lt;h2 id=&quot;why-this-matters-beyond-one-skill&quot;&gt;Why this matters beyond one skill&lt;/h2&gt;

&lt;p&gt;The bigger point isn’t about CircleCI or Claude Code specifically.
There are a few broad lessons here:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Prefer tools to raw APIs&lt;/li&gt;
  &lt;li&gt;Design CLI tools to be discoverable&lt;/li&gt;
  &lt;li&gt;Design CLI tools for token-efficient output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s look at each one.&lt;/p&gt;

&lt;h3 id=&quot;prefer-tools-to-raw-apis&quot;&gt;Prefer tools to raw APIs&lt;/h3&gt;

&lt;p&gt;If your agent has to reach an external system through a general-purpose HTTP tool, it’s writing REST calls by hand, dealing with caching, composing URLs, and parsing JSON shapes.
Every one of those steps is a place the agent can go wrong.&lt;/p&gt;

&lt;p&gt;If instead the agent can reach the same system through a well-designed CLI, the CLI hides all of that.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run watch&lt;/code&gt; doesn’t need cache-busting.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci artifact --output&lt;/code&gt; doesn’t need URL composition.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job output get --condensed&lt;/code&gt; doesn’t need the agent to figure out how to trim noise for an LLM — the vendor already did.&lt;/p&gt;

&lt;p&gt;A CLI aimed at agents is a shorter path than the raw API, and shorter paths are more reliable.
When you’re writing skills, prefer the CLI when one exists — and let the absence of one push you to open an issue with the vendor rather than to hand-craft another URL.&lt;/p&gt;

&lt;h3 id=&quot;design-cli-tools-to-be-discoverable-in-the-era-of-shallow-understanding&quot;&gt;Design CLI tools to be discoverable in the era of shallow understanding&lt;/h3&gt;

&lt;p&gt;It’s also worth pointing out that Claude Code figured out how to use the CircleCI CLI without me telling it anything about the commands.
It simply ran &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci --help&lt;/code&gt;, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci run --help&lt;/code&gt;, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;circleci job --help&lt;/code&gt;, and so on, until it had a mental model of the CLI’s command tree.
That was enough for it to determine the right commands and update the skill.
If you are developing CLI tools, make sure they are discoverable in this way — the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; output is a first-class part of the interface, not just a convenience for humans.&lt;/p&gt;

&lt;h3 id=&quot;design-cli-tools-for-token-efficient-output&quot;&gt;Design CLI tools for token-efficient output&lt;/h3&gt;

&lt;p&gt;Finally, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--condensed&lt;/code&gt; flag is a great example of a CLI designed for an agent’s needs.
Filtering noise server-side lowers token cost &lt;em&gt;and&lt;/em&gt; keeps the agent’s context window focused on what matters — and the vendor knows which lines are noise better than any skill author does.
Worth considering whenever you design an API or CLI tool.&lt;/p&gt;

&lt;h2 id=&quot;about-this-article&quot;&gt;About this article&lt;/h2&gt;

&lt;p&gt;Claude Code wrote the first draft of this post during the same session that made the change.
The skill migration and the article came out of one conversation.
First, I asked Claude to migrate the skill to the CircleCI CLI tool.
After that, I told it to draft this post.&lt;/p&gt;

&lt;h2 id=&quot;need-help-with-modernizing-your-architecture&quot;&gt;Need help with modernizing your architecture?&lt;/h2&gt;

&lt;p&gt;I help organizations modernize their architecture to enable fast flow and GenAI-powered software delivery. If you’re planning or struggling with a modernization effort, I can help.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://chrisrichardson.net&quot;&gt;Learn more about my modernization and architecture advisory work →&lt;/a&gt;&lt;/p&gt;

</description>
            <pubDate>Fri, 17 Jul 2026 08:03:00 +0000</pubDate>
            <link>http://microservices.io//post/deployment-pipeline/2026/07/17/from-webfetch-to-circleci-cli-lessons-learned.html</link>
            <guid isPermaLink="true">http://microservices.io//post/deployment-pipeline/2026/07/17/from-webfetch-to-circleci-cli-lessons-learned.html</guid>
        </item>
        
    </channel>
</rss>
