<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mark Fussell</title>
    <description>The latest articles on DEV Community by Mark Fussell (@mfussell).</description>
    <link>https://dev.to/mfussell</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1026869%2F37ada0d7-1cac-4f7a-a825-b53a2663e585.jpeg</url>
      <title>DEV Community: Mark Fussell</title>
      <link>https://dev.to/mfussell</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mfussell"/>
    <language>en</language>
    <item>
      <title>MCP Is the WSDL Moment for Agents</title>
      <dc:creator>Mark Fussell</dc:creator>
      <pubDate>Tue, 27 Oct 2026 03:26:59 +0000</pubDate>
      <link>https://dev.to/mfussell/mcp-is-the-wsdl-moment-for-agents-157d</link>
      <guid>https://dev.to/mfussell/mcp-is-the-wsdl-moment-for-agents-157d</guid>
      <description>&lt;p&gt;&lt;em&gt;Web services standardised how to describe a service, then spent a decade learning that identity, reliability and recovery don't belong in the message format. MCP's maintainers have learned that lesson. Every team running an MCP server still has to.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In 2001 the W3C published WSDL, the Web Services Description Language. A WSDL file described what a service could do: its operations, its message formats and where to send them. Feed it to a tool and you got a working client. Two companies that had never met could integrate in an afternoon. I was at Microsoft through those years and was fully engaged in the WS-* specification standards bodies, and the excitement was real.&lt;/p&gt;

&lt;p&gt;The Model Context Protocol does the same job for agents. An MCP server wraps a database, an API or a file system and tells any MCP client which tools it has, what arguments each takes and what it returns. Messages are JSON-RPC 2.0, over STDIO on the same machine or Streamable HTTP across a network. Write the server once and every MCP-compatible agent can use it. That removes the M-by-N problem function calling left us with, where M AI applications and N tools meant wiring each pair by hand.&lt;/p&gt;

&lt;p&gt;So far, so WSDL. Here is where I think the story diverges: MCP will win where WSDL didn't, because it is choosing to stay thin. That puts the hard problems somewhere else, and anyone shipping an MCP server has to decide where.&lt;/p&gt;

&lt;h2&gt;
  
  
  What description didn't cover
&lt;/h2&gt;

&lt;p&gt;Describing a service turned out to be the easy part. Once WSDL made integration cheap, everyone asked the next questions. Who is allowed to call this operation? What happens if the call is delivered twice? What if the caller crashes halfway through a five-step exchange? How do I find out what happened?&lt;/p&gt;

&lt;p&gt;The industry answered by writing more specifications: WS-Security, WS-ReliableMessaging, WS-AtomicTransaction, WS-Coordination and a long tail after them. I was in the standards bodies for all of these, implementing them in Microsoft products. Each was reasonable on its own. Together they made a stack few teams could implement and fewer could debug. It was a specification nightmare that only Microsoft and IBM could afford to do. The public UDDI registries, meant to be the directory of every web service on earth, were switched off in 2006.&lt;/p&gt;

&lt;p&gt;REST and JSON won the next decade by doing less and not having the insane complexity that WS-* involved. The hard problems moved out of the message format and into the runtime, which is where they belonged from the start: API gateways, service meshes, retry policies, mutual TLS and distributed tracing. And of course the Dapr project.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP chose the REST path
&lt;/h2&gt;

&lt;p&gt;The clearest sign is the 2026-07-28 revision of the Model Context Protocol, the largest since launch. Its direction is the opposite of WS-*: it takes things out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No more sessions.&lt;/strong&gt; The initialize handshake and the &lt;code&gt;Mcp-Session-Id&lt;/code&gt; header are gone. Every request carries its own protocol version and client capabilities, so any server instance can handle any call behind a plain load balancer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State moves to the application.&lt;/strong&gt; A server that needs continuity hands the model an explicit handle, a basket ID or a browser ID, and gets it back as an ordinary argument on the next call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less in the core.&lt;/strong&gt; Tasks, the long-running work feature, moved out of the core spec into an opt-in extension. Roots, Sampling and Logging are deprecated, with OpenTelemetry named as the home for observability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built for infrastructure.&lt;/strong&gt; New &lt;code&gt;Mcp-Method&lt;/code&gt; and &lt;code&gt;Mcp-Name&lt;/code&gt; headers let gateways route and rate-limit without reading the body, and W3C Trace Context in the request metadata lets traces follow a tool call end to end. We applied exactly the same approach in Dapr.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the REST lesson applied on purpose: the protocol defines the contract, and the infrastructure around it does the rest. New capabilities now arrive as extensions that are negotiated and versioned separately, rather than as specs every implementation must carry. WS-* never had that release valve. Whether extensions stay optional is the thing to watch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What every MCP server in production still has to answer
&lt;/h2&gt;

&lt;p&gt;A thinner protocol doesn't make the WSDL-era questions go away. It hands them to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who may call which tool.&lt;/strong&gt; The spec now treats an MCP server as an OAuth 2.1 resource server, which settles how a client proves who it is. It doesn't decide which of a server's forty tools that client may invoke, or where the upstream API key for your CRM lives. Too often that key still sits in agent code or an environment variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the tool says it does.&lt;/strong&gt; A tool description is text the model reads as instruction. Security teams spent 2026 warning about tool poisoning, where a description is altered after approval to steer an agent into leaking data. Nothing in the protocol stops a server from changing what it tells the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens on failure.&lt;/strong&gt; Picture an agent that calls a refund tool, then crashes before recording the result. It restarts and sees an unfinished task. Does it refund again? A stateless protocol is the right design, but it means the answer lives entirely in your application. Retries, timeouts and circuit breakers are yours too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which servers to trust.&lt;/strong&gt; The official MCP Registry is thriving where UDDI died, with roughly 39,000 servers listed by early October. But a registry is a namespace, not a trust decision. Choosing which MCP server your agents may reach is still a runtime question, and it is critical to security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the answers belong
&lt;/h2&gt;

&lt;p&gt;The runtime around the MCP server is the right home for each of those questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt; comes from the platform, with deny-by-default rules per tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Credentials&lt;/strong&gt; are injected at request time, if they're needed at all once identity is federated, and never sit in the agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retries and timeouts&lt;/strong&gt; are policy, written once, not code in every application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Each tool call&lt;/strong&gt; is recorded as a step in a durable workflow, so an agent that crashes after the refund resumes past it instead of making it twice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A wave of MCP gateways now handles the first three, and that instinct is right. But a gateway sees a request, not the workflow it belongs to. Recovering from a crash means the call has to be recorded somewhere that outlives the agent process.&lt;/p&gt;

&lt;p&gt;That is the approach we took with Diagrid Catalyst. It fronts MCP servers with proxy endpoints that speak Streamable HTTP and the stateless 2026-07-28 revision, so off-the-shelf clients and agent frameworks work unchanged. Authentication, credential injection and per-tool access rules happen outside the agent, and every tool call runs as a durable workflow step. Dapr does the same for everything else the agent touches: state, messaging and service calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thin protocol wins
&lt;/h2&gt;

&lt;p&gt;I expect the model context protocol to become as universal as WSDL wanted to be, because it is staying as thin as WSDL should have. MCP servers will number in the hundreds of thousands, and the engineering that decides whether they are safe to run will happen around them.&lt;/p&gt;

&lt;p&gt;If you're building an MCP server this quarter, decide now where its caller identity, credentials, retries and crash recovery come from. The spec won't decide it for you, and its maintainers have made clear it shouldn't.&lt;/p&gt;

&lt;p&gt;A longer explainer on how MCP works, with the architecture: &lt;a href="https://www.diagrid.io/learn/model-context-protocol-mcp" rel="noopener noreferrer"&gt;Model Context Protocol (MCP) Explained&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/" rel="noopener noreferrer"&gt;The 2026-07-28 MCP Specification Release Candidate&lt;/a&gt;, MCP blog&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/modelcontextprotocol/specification/releases/tag/2026-07-28" rel="noopener noreferrer"&gt;MCP 2026-07-28 stable release&lt;/a&gt;, GitHub&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gravity.fast/data/mcp-adoption-statistics/" rel="noopener noreferrer"&gt;MCP adoption statistics&lt;/a&gt; (registry count, October 4, 2026)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://labs.cloudsecurityalliance.org/research/csa-research-note-mcp-tool-poisoning-ai-agent-exfiltration-2/" rel="noopener noreferrer"&gt;MCP tool poisoning research note&lt;/a&gt;, Cloud Security Alliance&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>The Saga Pattern Was a Workaround</title>
      <dc:creator>Mark Fussell</dc:creator>
      <pubDate>Fri, 09 Oct 2026 17:33:44 +0000</pubDate>
      <link>https://dev.to/mfussell/the-saga-pattern-was-a-workaround-42fi</link>
      <guid>https://dev.to/mfussell/the-saga-pattern-was-a-workaround-42fi</guid>
      <description>&lt;p&gt;&lt;em&gt;We spent a decade hand-building compensation logic because our runtimes couldn't remember where they were. Now they can.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The saga pattern is older than most of the systems it runs in. Hector Garcia-Molina and Kenneth Salem published it in 1987 for long-lived database transactions. The microservices era rediscovered it around 2015, and I spent a good part of the following decade explaining it on stage: when a business transaction spans services and step four fails, you can't roll back a distributed system, so you run compensating actions (refund the charge, release the inventory, cancel the booking) in reverse order.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the saga pattern?
&lt;/h2&gt;

&lt;p&gt;A saga is a sequence of local transactions in which every step has a defined compensating action. If a step fails, the saga runs the compensations for every step that already completed. It trades the atomicity you lost when you distributed the system for eventual consistency that you manage yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a saga really is
&lt;/h2&gt;

&lt;p&gt;Sagas work. Payment, travel, and retail systems run on them today. But be honest about what a saga actually is: a hand-built answer to the question "what happens when the process dies halfway through?"&lt;/p&gt;

&lt;p&gt;To answer it, you maintain four things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Compensation logic&lt;/strong&gt; for every step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A state machine&lt;/strong&gt; that tracks which step you're on&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recovery code&lt;/strong&gt; that reads that state after a crash&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency&lt;/strong&gt; so a retried compensation doesn't fire twice&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every team builds this machinery, slightly differently, with the same bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What durable execution changes
&lt;/h2&gt;

&lt;p&gt;We built sagas this way because the runtime gave us nothing better. That premise has expired.&lt;/p&gt;

&lt;p&gt;Durable execution journals every step as it runs. A crash resumes from the exact step that failed, completed steps never re-execute, and compensation becomes ordinary workflow code: recorded, replayable, and visible, instead of tribal machinery around the edges. The state machine you used to maintain by hand is now the engine.&lt;/p&gt;

&lt;p&gt;Sagas don't disappear. The business reality they encode (some actions can only be undone, not prevented) is permanent. What disappears is the plumbing. On a durable workflow runtime, a saga is a dozen lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;order_saga&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DaprWorkflowContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;compensations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reserve_inventory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;compensations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;release_inventory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;charge_payment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;compensations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refund_payment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book_shipping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;undo&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compensations&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_activity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;undo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try the steps, catch the failure, run the compensations, all of it journaled. One of my favourite demos is deleting two-thirds of a team's saga code and watching the remaining third become readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why agents raise the stakes
&lt;/h2&gt;

&lt;p&gt;Agents make all of this matter more. An agent's compensations guard real money and real customer data. Its steps are expensive LLM calls you'd rather not re-run. And its failure points multiply with its autonomy: an agent that books a flight, charges a card, and then fails to reserve the hotel needs to unwind cleanly, not leave a customer half-booked.&lt;/p&gt;

&lt;p&gt;If sagas were worth teaching through a decade of microservices, their durable-execution successor is worth learning in the first year of agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.diagrid.io/blog/code-like-a-monolith-scale-like-a-microservice-durable-workflows-for-cloud-native-apps" rel="noopener noreferrer"&gt;Code like a monolith, scale like a microservice: durable workflows for cloud-native apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.diagrid.io/blog/durable-workflows-ai-agents" rel="noopener noreferrer"&gt;Durable workflows for AI agents&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>distributedsystems</category>
      <category>microservices</category>
      <category>architecture</category>
      <category>ai</category>
    </item>
    <item>
      <title>Strip the Branding Off Your Agent Framework. It's the Same Five Lines.</title>
      <dc:creator>Mark Fussell</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/mfussell/strip-the-branding-off-your-agent-framework-its-the-same-five-lines-3a35</link>
      <guid>https://dev.to/mfussell/strip-the-branding-off-your-agent-framework-its-the-same-five-lines-3a35</guid>
      <description>&lt;p&gt;Strip the branding off your sgent framework and it's the same five lines.&lt;/p&gt;

&lt;p&gt;This will annoy some framework authors, so let me be clear about the claim. Take LangGraph, CrewAI, Strands, Google ADK, Pydantic or Microsoft Agent Framework. Remove the branding, the abstractions, the graph builders. At the core of every one of them you find variations of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;goal_reached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;thought&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# decide
&lt;/span&gt;    &lt;span class="n"&gt;action&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thought&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# choose a tool
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# the side effect happens HERE
&lt;/span&gt;    &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# remember
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five lines of code. The model is almost comically simple, and I mean that as a compliment, because simple models win. The Windows message loop (GetMessage, TranslateMessage, DispatchMessage) was about this same size. I wrote those for Windows 3.0 back in the 90s, so the shape is an old friend.&lt;/p&gt;

&lt;p&gt;Before the objections arrive: parallel tool calls just make action a list. Graph-based frameworks turn the while into edges between nodes. Multi-agent setups are loops calling loops. The shape holds.&lt;/p&gt;

&lt;p&gt;So why do agent frameworks feel different? Because they compete on everything around the loop: nicer tool definitions, multi-agent hand-offs, streaming, tracing. All genuinely useful, and all decoration on the same five lines. It is still a loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  The line that matters
&lt;/h2&gt;

&lt;p&gt;Whether your agent survives production isn't decided by the loop or the decoration. It's decided by line four. &lt;code&gt;execute(action)&lt;/code&gt; is where money moves, tickets open and emails get sent.&lt;/p&gt;

&lt;p&gt;When the process dies mid-loop (a deploy, an eviction, an OOM kill), someone has to answer two questions: which steps already completed, and where should we resume? The loop has no idea. Agent framework checkpoints help, but they snapshot state at intervals, so recovery often means rerunning work since the last snapshot, including side effects that already fired. No frameworks have a failure detection mechanism included, which is the really hard problem to solve. &lt;/p&gt;

&lt;p&gt;That's a runtime concern, and it has a name: durable execution. Here's the same loop on a durable runtime, in engine-agnostic pseudocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@workflow&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;history&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;goal_reached&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;thought&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# journaled
&lt;/span&gt;        &lt;span class="n"&gt;action&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thought&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                    &lt;span class="c1"&gt;# deterministic, safe to replay
&lt;/span&gt;        &lt;span class="n"&gt;result&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# journaled, retried if in flight
&lt;/span&gt;        &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvcjk4fvxg167wi8b5w8o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvcjk4fvxg167wi8b5w8o.png" alt="What happens when an agent crashes mid-loop" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each completed step is recorded in a journal. After a crash, the runtime replays the journal. Completed steps return their recorded results instantly, with no second LLM bill and no duplicate email, and execution resumes at the exact step that was in flight.&lt;/p&gt;

&lt;p&gt;One honest caveat: a step that crashes after its side effect fires but before the journal write will be retried. Durable execution gives you at-least-once steps, not magic. So your tools still need to be idempotent (pass an idempotency key to the payment API). The difference is that retries become precise and bounded instead of "rerun everything and hope."&lt;/p&gt;

&lt;h2&gt;
  
  
  The corollary
&lt;/h2&gt;

&lt;p&gt;If the loop is the same five lines everywhere, durability doesn't need to live in the framework. Keep the framework you like, and put the loop on a durable runtime. These are separable decisions, and the ecosystem is already heading that way, with durable-runtime integrations appearing for several popular frameworks.&lt;/p&gt;

&lt;p&gt;I'd go further: coupling the two is how the industry gets locked in for the next decade. This is workflow orchestration's oldest lesson wearing a new AI badge.&lt;/p&gt;

&lt;p&gt;I've written the longer history of where this loop came from in &lt;a href="https://www.diagrid.io/blog/agentic-execution-evolution-6-agent-loop" rel="noopener noreferrer"&gt;chapter 6 of my series&lt;/a&gt;, and there's a practical companion on &lt;a href="https://www.diagrid.io/blog/keep-your-agent-framework" rel="noopener noreferrer"&gt;keeping your framework while adding durable execution&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Disagree? Tell me which framework's core loop is genuinely a different shape. I'll happily be wrong.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>python</category>
    </item>
  </channel>
</rss>
