<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog.lavoie.sl/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.lavoie.sl/" rel="alternate" type="text/html" /><updated>2026-05-05T10:36:59-04:00</updated><id>https://blog.lavoie.sl/feed.xml</id><title type="html">Computers and stuff</title><subtitle>Software Engineering, People Management, and other things I find interesting.</subtitle><author><name>Sebastien Lavoie</name></author><entry><title type="html">Agentic systems are struggling to scale (this should feel familiar)</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-1-struggling-to-scale" rel="alternate" type="text/html" title="Agentic systems are struggling to scale (this should feel familiar)" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-1-struggling-to-scale</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-1-struggling-to-scale"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><strong><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<hr />

<p>The agents work. The system fails.</p>

<p>That is the pattern showing up across companies that tried to turn AI into a headcount strategy instead of a systems strategy.</p>

<p>Between 2023 and 2026, executives cut teams on the assumption that AI could absorb large parts of the workflow. The reversal came fast. <a href="https://www.forbes.com/councils/forbestechcouncil/2026/04/24/why-companies-regret-laying-off-workers-for-ai/" title="Why Companies Regret Laying Off Workers For AI - Forbes">Forbes</a> and <a href="https://www.hcamag.com/ca/news/general/businesses-rush-to-rehire-staff-after-regretted-ai-driven-cuts/568293" title="Businesses rush to rehire staff after regretted AI-driven cuts - Human Resources Director">Human Resources Director</a>, both drawing on the same rehiring pattern, point to the same outcome: companies tried to remove a workflow and discovered they had only removed the people holding it together. Careerminds survey data cited by HRD reports that <strong>two-thirds of companies are already rehiring roles they cut for AI</strong>. More than half said the rehiring began within six months.</p>

<p><a href="https://www.techrepublic.com/article/news-leaders-regret-ai-driven-layoffs/" title="'We Acted Too Quickly': Over Half of Companies Regret AI-Driven Layoffs, Report Finds - TechRepublic">TechRepublic</a> reports that <strong>55% of companies now regret AI-driven layoffs</strong>. <a href="https://www.computerworld.com/article/4084372/analysts-companies-will-face-setbacks-after-ai-layoffs.html" title="Forrester: Companies regret many AI-related layoffs - Computerworld">Computerworld</a>, summarizing Forrester, adds the missing diagnosis: too often management laid people off based on <strong>the future promise of AI</strong>, not on proven capability inside a working system.</p>

<blockquote>
  <p>The industry is not struggling because AI is weak, it’s because systems were removed faster than they could be replaced.</p>
</blockquote>

<p>It accelerated the system until the weak parts became impossible to ignore.</p>

<h2 id="the-first-public-failures-are-already-here">The first public failures are already here</h2>

<p>Klarna became the clearest public example. Its leadership said AI was handling work equivalent to roughly <strong>700 customer service agents</strong>, and the move was framed as evidence that replacement at scale had arrived. But as <a href="https://www.digitalapplied.com/blog/klarna-reverses-ai-layoffs-replacing-700-workers-backfired" title="Klarna Reverses AI Layoffs: Why Replacing 700 Failed - Digital Applied">Digital Applied’s Klarna analysis</a> summarizes, the routine volume held while customer satisfaction fell on the complex interactions that actually required judgment, escalation, and trust. Klarna then shifted back toward rehiring and a hybrid model.</p>

<p>It was the same system failure in a more visible form.</p>

<table>
  <thead>
    <tr>
      <th>Local gain</th>
      <th>System failure</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>AI handled routine volume</td>
      <td>Quality dropped on edge cases</td>
    </tr>
    <tr>
      <td>Staffing costs looked lower on paper</td>
      <td>Rehiring and correction costs erased savings</td>
    </tr>
    <tr>
      <td>Output arrived faster</td>
      <td>Judgment, escalation, and accountability became the constraint</td>
    </tr>
  </tbody>
</table>

<p>The failure is easy to misread because the models did useful work. The diagnosis was wrong.</p>

<p>Companies removed the human layer that interpreted ambiguity, handled exceptions, and preserved continuity across the workflow. Once that layer was gone, the system degraded.</p>

<h2 id="the-wrong-conclusion">The wrong conclusion</h2>

<p>The easy conclusion is that the models still are not capable enough, but that misses the failure mode.</p>

<p>The failure sits in the system the agent operates in.</p>

<p>What broke first was the control layer around the agent: intent definition, handoffs, validation, and exception handling.</p>

<h2 id="the-real-problem-is-coordination">The real problem is coordination</h2>

<p>The same pattern shows up in engineering teams building agentic systems.</p>

<p>An agent can write a function or scaffold something meaningful, but ask it to help ship a real feature, one that spans research, design, implementation, testing, and rollout, and the surrounding system starts to break down.</p>

<p>That happens because agentic systems today often:</p>

<ul>
  <li>Pass too much raw context</li>
  <li>Blur roles between planning and execution</li>
  <li>Lack clear handoffs and validation</li>
</ul>

<p>Those failures are connected. When work is passed forward as raw context instead of as a structured handoff, every downstream step has to reinterpret intent, constraints, and success criteria for itself. Local work speeds up, but system throughput does not improve.</p>

<p>This is not a model problem. It is a coordination problem.</p>

<h2 id="the-deeper-pattern-learning-from-autopilot">The deeper pattern: learning from autopilot</h2>

<p>As <a href="https://mwalterskirchen.dev/blog/piloting-agentic-engineering/" title="Piloting Agentic Engineering - What Software Engineers Can Learn From The Aviation Industry">Maximilian Walterskirchen’s essay on piloting agentic engineering</a> argues, autopilot did not simplify aviation. It forced the system around the cockpit to become more explicit.</p>

<p>Pilots were not removed from the loop. Their role shifted toward supervision, intervention, and takeover when automation no longer fit the situation. The new risk appeared at the handoff back to the human.</p>

<blockquote>
  <p>Automation does not remove the system. It shifts control within it.</p>
</blockquote>

<p>The same structural change is happening in software organizations. Agents do more local work, but the surrounding system still has to decide when to delegate, what to pass forward, how to validate results, and where human judgment remains necessary.</p>

<p>The surrounding systems are failing under acceleration.</p>

<h2 id="the-key-insight-ai-amplifies-the-system">The key insight: AI amplifies the system</h2>

<p>The <a href="https://www.thoughtworks.com/insights/reports/the-2025-dora-report" title="The 2025 DORA report - Thoughtworks">2025 DORA report</a> names the principle directly: <strong>“AI is an amplifier, not a fix.”</strong> Faster drafting does not repair unclear ownership, brittle reviews, or weak validation. It exposes them.</p>

<p>The recurring debate about smarter models misses the main constraint.</p>

<p>The real question is not:</p>

<blockquote>
  <p>How do we make agents smarter?</p>
</blockquote>

<p>It is:</p>

<blockquote>
  <p>How do we structure systems so intelligence can scale without losing control?</p>
</blockquote>

<p>This series argues that agentic systems are fundamentally a systems engineering problem.</p>

<h2 id="the-next-question">The next question</h2>

<p>If faster agents still yield brittle systems, the problem is not a lack of intelligence. It is that too much of the work is still being held together by operators compensating in real time.</p>

<p>That makes the field’s maturity hard to ignore, because so many systems still depend on craft more than design.</p>

<p>👉 <a href="/2026/05/agentic-systems-2-artisanal-era">Part 2: Agentic systems are still in the artisanal era</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Why companies that cut for AI are rehiring: the models can help, but agentic systems fail when coordination, validation, and human handoffs disappear.]]></summary></entry><entry><title type="html">Agentic systems are still in the artisanal era</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-2-artisanal-era" rel="alternate" type="text/html" title="Agentic systems are still in the artisanal era" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-2-artisanal-era</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-2-artisanal-era"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><strong><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>A great demo is not engineering.</p>

<p>Agentic systems can look convincing right up until they meet real operating conditions.</p>

<p>The layoff reversals from Part 1 made the problem visible: companies automated visible tasks before they engineered the workflow around them.</p>

<p>The field can produce impressive one-off results. What it still lacks, in most cases, is the operating discipline that makes those results repeatable.</p>

<h2 id="craft-works-until-it-doesnt">Craft works until it doesn’t</h2>

<p>Early in any field, systems are built through skill, intuition, and iteration.</p>

<p>They rely on:</p>

<ul>
  <li>Individual expertise</li>
  <li>Informal workflows</li>
  <li>Trial and error</li>
</ul>

<p>That is still where agentic systems are.</p>

<p>Success often depends on someone who knows how to phrase prompts, recover from failures, and manually steer execution back on course. That is not yet a robust operating model. It is a skilled operator compensating for missing structure.</p>

<p>Companies that removed humans early ran into exactly that: they mistook operator effort for system design. The hidden coordination work was still there. It only became visible once the system started failing.</p>

<h2 id="the-signs-of-an-artisanal-system">The signs of an artisanal system</h2>

<p>You can recognize this stage quickly. The system works, but:</p>

<ul>
  <li>Results vary depending on who operates it</li>
  <li>Knowledge is implicit instead of encoded</li>
  <li>Recovery is manual instead of built into the workflow</li>
  <li>Scaling requires more intervention instead of more reuse</li>
</ul>

<p>In engineering terms, the system is not repeatable.</p>

<p><a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" title="Effective context engineering for AI agents - Anthropic">Anthropic</a> describes the operational root of this problem directly: context is a finite resource, so systems have to decide what to carry forward and what to compress. If that decision still lives in one operator’s head, the workflow has not matured.</p>

<h2 id="the-guidance-is-already-shifting">The guidance is already shifting</h2>

<p>By 2025 and 2026, the official guidance had already moved away from prompt craft as the main design surface.</p>

<p>The shift is from improvisation to workflow engineering:</p>

<ul>
  <li><a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" title="Effective context engineering for AI agents - Anthropic">Anthropic</a> calls the problem context engineering: deciding what information the system should carry forward, in what form, and for which next step.</li>
  <li><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">OpenAI</a> recommends starting with the simplest agent that can work and only adding more specialization when prompt complexity, tool overload, or task divergence genuinely require it.</li>
</ul>

<p>Prompt skill should not stand in for the system.</p>

<p>That does not mean every engineering task should be documented like a training manual. The leverage is in making recurring, cross-role, or high-consequence handoffs explicit enough that the work can be resumed, reviewed, and delegated without one operator carrying all the state.</p>

<h2 id="from-craft-to-engineering">From craft to engineering</h2>

<p>This transition is familiar because every engineering field goes through it.</p>

<table>
  <thead>
    <tr>
      <th>Artisanal phase</th>
      <th>Engineered phase</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Operator memory</td>
      <td>Explicit artifacts</td>
    </tr>
    <tr>
      <td>Prompt heroes</td>
      <td>Defined roles</td>
    </tr>
    <tr>
      <td>Manual recovery</td>
      <td>Validation and repeatable handoffs</td>
    </tr>
  </tbody>
</table>

<p>Agentic systems are still early enough that outcomes often depend more on how the workflow is operated than on how it is designed.</p>

<p>That dependence on operation over design is what immaturity looks like, and maturity only makes the next problem easier to see.</p>

<h2 id="the-next-pressure">The next pressure</h2>

<p>Moving from craft to structure makes a system more repeatable, but it also makes its limits easier to see.</p>

<p>Once work has to move across multiple stages, throughput is set by sequencing, coordination, and the slowest point in the flow.</p>

<p>👉 <a href="/2026/05/agentic-systems-3-fundamental-limits">Part 3: Agentic systems are bound by the same fundamental limits</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Agentic systems still operate like craft work: impressive demos depend on operator skill, implicit knowledge, and manual recovery instead of engineered workflows.]]></summary></entry><entry><title type="html">Agentic systems are bound by the same fundamental limits</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-3-fundamental-limits" rel="alternate" type="text/html" title="Agentic systems are bound by the same fundamental limits" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-3-fundamental-limits</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-3-fundamental-limits"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><strong><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>Brittleness explains part of what teams are feeling. It does not remove the deeper limits that govern any workflow once it has to scale.</p>

<p>Every system that scales eventually runs into the same reality: the constraint defines the system.</p>

<p>These are not AI-native limits. Engineering has been running into them for decades.</p>

<h2 id="capacity-is-not-throughput">Capacity is not throughput</h2>

<p>The default assumption is simple: add more capacity, get more output. Add more agents, get more work done.</p>

<p>Many AI replacement efforts rest on exactly that assumption, which is why those efforts reversed so quickly. Adding more AI capacity did not remove escalation, approval, testing, or exception handling. It only pushed more work toward those stages.</p>

<p>Adding more agents still did not increase throughput in real systems. Local work sped up. The constrained stages did not.</p>

<h2 id="two-constraints-one-outcome">Two constraints, one outcome</h2>

<p><a href="https://www3.cs.stonybrook.edu/~rezaul/Spring-2012/CSE613/reading/Amdahl-1967.pdf" title="Validity of the single processor approach to achieving large scale computing capabilities - Gene M. Amdahl">Amdahl’s Law</a> and modern delivery bottlenecks describe the same system from two angles.</p>

<table>
  <thead>
    <tr>
      <th>Constraint</th>
      <th>What it means</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Serial fraction</td>
      <td>Some work must still happen in order</td>
      <td>Requirement clarification, approval, deployment</td>
    </tr>
    <tr>
      <td>Bottleneck stage</td>
      <td>One constrained stage sets throughput</td>
      <td>Code review, integration, compliance review</td>
    </tr>
  </tbody>
</table>

<p><a href="https://www3.cs.stonybrook.edu/~rezaul/Spring-2012/CSE613/reading/Amdahl-1967.pdf" title="Validity of the single processor approach to achieving large scale computing capabilities - Gene M. Amdahl">Amdahl’s 1967 paper</a> says the maximum improvement to a system is limited by the fraction that remains serial. <a href="https://www.atlassian.com/blog/ai-at-work/how-amdahls-law-still-applies-to-modern-day-ai-inefficiencies" title="How Amdahl's law still applies to modern-day AI inefficiencies - Atlassian">Atlassian’s modern explainer</a> makes the team version concrete: if only about 20% of the lifecycle is individual work, even <strong>“instant”</strong> AI on that slice only raises overall throughput to about <strong>1.25x</strong> because the other 80% still moves at normal system speed.</p>

<p>Operationally, the same limit shows up as a queue.</p>

<p>If review is slow, faster drafting creates a review queue. If integration is slow, faster implementation creates an integration queue. If approvals are slow, faster planning creates an approval queue. The form changes, but the underlying constraint does not.</p>

<h2 id="why-agentic-systems-hit-this-early">Why agentic systems hit this early</h2>

<p>Agentic systems often try to scale by increasing what is easiest to increase:</p>

<ul>
  <li>More agents</li>
  <li>More parallel tasks</li>
  <li>More intermediate output</li>
</ul>

<p>That improves local productivity without automatically improving delivery.</p>

<p>As <a href="https://www.atlassian.com/blog/ai-at-work/how-amdahls-law-still-applies-to-modern-day-ai-inefficiencies" title="How Amdahl's law still applies to modern-day AI inefficiencies - Atlassian">Atlassian</a> notes, once AI accelerates drafting, the system starts accumulating work at reviews, approvals, and risk checks. <a href="https://newsletter.eng-leadership.com/p/code-review-is-the-new-bottleneck" title="Code review is the new bottleneck - Engineering Leadership Newsletter">The Engineering Leadership Newsletter</a> makes the same point with one concrete example: code review can become the bottleneck even when implementation gets faster.</p>

<p>That is why the layoff-and-rehiring cycle from Part 1 matters so much. The failure was not that AI produced nothing. The failure was that the system still depended on narrow stages where judgment, escalation, and validation lived.</p>

<h2 id="scaling-means-working-the-constraint">Scaling means working the constraint</h2>

<p>The mistake is optimizing whatever looks most scalable.</p>

<p>Parallel work is usually easy to add. Sequential dependencies and bottlenecks are not.</p>

<p>Effective systems focus on:</p>

<ul>
  <li>Reducing unnecessary serial dependencies</li>
  <li>Identifying the real bottleneck</li>
  <li>Controlling what reaches constrained stages</li>
</ul>

<p>Most of the system can get faster without the system moving faster at all.</p>

<h2 id="what-the-bottleneck-changes">What the bottleneck changes</h2>

<p>Once constrained stages define throughput, raw upstream context becomes a liability.</p>

<p>You cannot ask every downstream step to reread everything and still expect the system to move. What crosses the bottleneck has to be smaller, sharper, and easier to verify than the full upstream discussion.</p>

<p>👉 <a href="/2026/05/agentic-systems-4-artifacts-are-compression">Part 4: Artifacts are compression: how systems handle complexity</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Adding more agents does not remove serial work or bottlenecks. Agentic systems hit the same throughput limits, queues, and constraints as any workflow.]]></summary></entry><entry><title type="html">Artifacts are compression: how systems handle complexity</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-4-artifacts-are-compression" rel="alternate" type="text/html" title="Artifacts are compression: how systems handle complexity" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-4-artifacts-are-compression</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-4-artifacts-are-compression"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><strong><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>Once a system has real bottlenecks, handoffs stop being optional paperwork because downstream stages are constrained.</p>

<p>If a downstream stage has to keep rereading and reinterpreting everything upstream, the bottleneck has not gone away. It has simply moved.</p>

<p>As systems grow, the amount of available information grows faster than the ability to process it. <a href="https://www.pinecone.io/learn/chunking-strategies/" title="Chunking Strategies for LLM Applications - Pinecone">Pinecone</a> notes that even long-context models suffer from the lost-in-the-middle problem, where buried information gets missed. <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" title="Effective context engineering for AI agents - Anthropic">Anthropic</a> turns that into an operating rule: context is finite, so systems should pass forward the smallest set of high-signal tokens rather than the whole transcript.</p>

<p>If your system depends on full context, it is already broken.</p>

<h2 id="artifacts-are-compression">Artifacts are compression</h2>

<p>Complex systems do not pass raw information forward; they compress it into artifacts.</p>

<p>Healthy delivery systems already do this. <a href="https://www.atlassian.com/agile/product-management/requirements" title="What is a product requirements document? - Atlassian">Atlassian’s PRD guide</a> describes the PRD as a single source of truth for purpose, features, user needs, and success criteria. The same logic applies to specs, designs, test plans, and release evidence.</p>

<table>
  <thead>
    <tr>
      <th>Artifact</th>
      <th>What it preserves</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Product requirements document</td>
      <td>Purpose, user needs, and success criteria</td>
    </tr>
    <tr>
      <td>Specification or design doc</td>
      <td>Constraints, interfaces, and decisions</td>
    </tr>
    <tr>
      <td>Test plan</td>
      <td>What must be validated before release</td>
    </tr>
    <tr>
      <td>Diff and release evidence</td>
      <td>What changed and how to verify it</td>
    </tr>
  </tbody>
</table>

<p>These are not documentation extras. They are the interfaces that keep downstream work from rediscovering upstream intent.</p>

<h2 id="compression-and-expansion">Compression and expansion</h2>

<p>Artifacts move through a recurring pattern: each stage expands the current artifact through research, planning, or execution, then recompresses the result into something the next stage can actually use.</p>

<ul>
  <li>Research turns raw signals into a problem framing</li>
  <li>Product framing turns that into requirements</li>
  <li>Design turns requirements into concrete solutions</li>
  <li>Engineering turns solutions into code, tests, and release evidence</li>
</ul>

<p><a href="https://www.ibm.com/docs/en/engineering-lifecycle-management-suite/doors-next/7.2.0?topic=requirements-traceability" title="Requirements traceability - IBM">IBM’s traceability guidance</a> shows how that flow stays coherent: requirements, implementation, and test artifacts remain linked so downstream stages can work from the current artifact instead of the entire upstream discussion.</p>

<h2 id="what-makes-an-artifact-effective">What makes an artifact effective</h2>

<p>Artifacts only reduce coordination cost if they are designed well.</p>

<table>
  <thead>
    <tr>
      <th>Property</th>
      <th>Why it matters</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Lossy but intentional</td>
      <td>They preserve what matters for the next step</td>
    </tr>
    <tr>
      <td>Role-specific</td>
      <td>Different stages need different representations</td>
    </tr>
    <tr>
      <td>Structured</td>
      <td>Consistency makes them easy to consume and validate</td>
    </tr>
    <tr>
      <td>Traceable</td>
      <td>Later stages can verify they still satisfy earlier intent</td>
    </tr>
  </tbody>
</table>

<p>Without these properties, the handoff degrades back into raw context.</p>

<h2 id="where-agentic-systems-go-wrong">Where agentic systems go wrong</h2>

<p>Many agentic systems still rely on large prompts with mixed context, loosely structured outputs, and a single agent spanning multiple roles.</p>

<p>That removes the structure <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents" title="Effective context engineering for AI agents - Anthropic">Anthropic</a> recommends: compact context, explicit compaction, and clear boundaries around what each step should receive. It asks each stage to recover intent from a transcript instead of receiving a handoff that already encodes the objective, constraints, and output shape.</p>

<p>The result is predictable: outputs drift, reasoning repeats, and ambiguity moves downstream.</p>

<h2 id="why-this-still-is-not-enough">Why this still is not enough</h2>

<p>A good artifact reduces ambiguity, but by itself it is only a local act of compression.</p>

<p>It becomes operational only when there is an ordered flow of stages that know when to expand it, when to validate it, and when to pass it on. Without that structure, even good documents stay inert.</p>

<p>👉 <a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Part 5: Why all systems become pipelines</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Artifacts compress complexity for the next stage. Specs, PRDs, test plans, and release evidence keep agentic systems from passing raw context downstream.]]></summary></entry><entry><title type="html">Why all systems become pipelines</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-5-why-all-systems-become-pipelines" rel="alternate" type="text/html" title="Why all systems become pipelines" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-5-why-all-systems-become-pipelines</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-5-why-all-systems-become-pipelines"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><strong><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>Pipelines are easy to mistake for bureaucracy until the work actually has to survive multiple stages.</p>

<p>Artifacts only work because pipelines exist.</p>

<p>Not every exploratory task needs one, but once work becomes multi-stage, interdependent, and high-consequence, staged flow stops being optional.</p>

<p><a href="https://www.nasa.gov/reference/systems-engineering-handbook/" title="Systems Engineering Handbook - NASA">NASA’s systems engineering handbook</a> and <a href="https://www.anthropic.com/engineering/building-effective-agents" title="Building effective agents - Anthropic">Anthropic’s guide on building effective agents</a> describe different domains, but both converge on the same point: scaled work moves through explicit stages with different responsibilities and outputs.</p>

<h2 id="why-stages-appear">Why stages appear</h2>

<p>Pipelines are not a stylistic choice; they are a response to how complex work behaves.</p>

<table>
  <thead>
    <tr>
      <th>Force</th>
      <th>Why it creates stages</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Specialization</td>
      <td>Different stages need different kinds of reasoning</td>
    </tr>
    <tr>
      <td>Dependency</td>
      <td>Some work cannot begin until earlier decisions exist</td>
    </tr>
    <tr>
      <td>Risk management</td>
      <td>Early stages reduce uncertainty before later stages commit resources</td>
    </tr>
  </tbody>
</table>

<p>Those pressures keep reappearing even when the vocabulary changes.</p>

<h2 id="convergence-across-systems">Convergence across systems</h2>

<p>Software delivery moves from planning to design to implementation to testing to deployment. <a href="https://www.nasa.gov/reference/systems-engineering-handbook/" title="Systems Engineering Handbook - NASA">NASA</a> describes comparable lifecycle stages for systems engineering, while <a href="https://www.anthropic.com/engineering/building-effective-agents" title="Building effective agents - Anthropic">Anthropic</a> describes workflow patterns such as chaining, routing, orchestrator-workers, and evaluator loops. The vocabulary changes, but the requirement does not: multi-stage work needs explicit transitions.</p>

<h2 id="the-canonical-flow">The canonical flow</h2>

<p>While terminology varies, most systems still look broadly like this:</p>

<pre><code class="language-mermaid">graph LR
Research --&gt; Planning --&gt; Design --&gt; Implementation --&gt; Testing --&gt; Deployment --&gt; Support
</code></pre>

<p>Each stage consumes an artifact, transforms it, and emits a new artifact for the next stage.</p>

<p>Pipelines matter because they are not just sequences of tasks. They are coordination mechanisms for turning one usable representation of work into the next.</p>

<h2 id="where-agentic-systems-drift">Where agentic systems drift</h2>

<p>Many agentic systems still ignore this shape. They rely on:</p>

<ul>
  <li>A single agent handling multiple stages</li>
  <li>Loosely defined transitions between steps</li>
  <li>Minimal separation between planning and execution</li>
</ul>

<p>It can look flexible in a demo, but it scales poorly. Work loops, direction drifts, and validation gets deferred.</p>

<p><a href="https://www.anthropic.com/engineering/building-effective-agents" title="Building effective agents - Anthropic">Anthropic</a> explicitly recommends simple, composable patterns instead of unnecessary framework complexity for exactly this reason.</p>

<h2 id="security-is-another-reason-stages-appear">Security is another reason stages appear</h2>

<p>Prompt injection makes the security case concrete. <a href="https://genai.owasp.org/llmrisk/llm01-prompt-injection/" title="LLM01:2025 Prompt Injection - OWASP GenAI Security Project">OWASP</a> treats indirect prompt injection as a major risk for systems that read external content and recommends least-privilege access plus human approval for high-risk actions.</p>

<p>That principle should already feel familiar from human systems. <a href="https://csrc.nist.gov/glossary/term/least_privilege" title="Least privilege - NIST Computer Security Resource Center">NIST</a> recommends only giving users, or processes acting on their behalf, the least privilege needed for assigned tasks. In practice, a stage that reads broadly from untrusted sources should produce an artifact, not take broad write actions; review happens there, and only later stages get narrower permissions.</p>

<h2 id="what-pipelines-still-do-not-solve">What pipelines still do not solve</h2>

<p>A pipeline explains how work moves. It does not explain why that flow survives change.</p>

<p>Stages can be clear and still remain fragile if their meaning lives in one operator, one model, or one transcript. Durability comes from boundaries that can survive changing tools, teams, and requirements.</p>

<p>👉 <a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Part 6: Long-lived systems need modularity</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Complex, high-stakes work converges on pipelines. Agentic systems need explicit stages, transitions, and outputs once work spans multiple dependent steps.]]></summary></entry><entry><title type="html">Long-lived systems need modularity</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-6-long-lived-systems-need-modularity" rel="alternate" type="text/html" title="Long-lived systems need modularity" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-6-long-lived-systems-need-modularity</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-6-long-lived-systems-need-modularity"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><strong><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>Pipelines explain how complex work moves, but modularity explains what lets a system survive change.</p>

<p>Long-lived systems need modularity because tools change, models change, teams change, and workflows still have to keep working.</p>

<blockquote>
  <p>You cannot modularize what is not already structured.</p>
</blockquote>

<p>If the stages, handoffs, and responsibilities are still ambiguous, there is nothing stable to make replaceable.</p>

<h2 id="modularity-is-survivability">Modularity is survivability</h2>

<p>A pipeline can be clear and still be fragile if planning logic lives in one operator’s head, approval criteria only exist in one long transcript, or recovery depends on one coordinating agent.</p>

<table>
  <thead>
    <tr>
      <th>Without modularity</th>
      <th>With modularity</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Changing one tool destabilizes the whole workflow</td>
      <td>Components can change behind stable interfaces</td>
    </tr>
    <tr>
      <td>Knowledge is trapped in one actor or session</td>
      <td>Knowledge is carried in explicit handoffs</td>
    </tr>
    <tr>
      <td>Failures spread across the system</td>
      <td>Blast radius stays local</td>
    </tr>
  </tbody>
</table>

<p>Bus-factor problems are really design problems. The risk is not merely that someone leaves. The risk is that the workflow was never encoded in a form the rest of the system could reuse.</p>

<h2 id="good-boundaries-localize-change">Good boundaries localize change</h2>

<p>Good modularity means small, substitutable parts with a clean separation between interface and implementation.</p>

<p>The key question is not whether the boxes are large or small. It is whether change stays local.</p>

<p>A good boundary lets you replace one part without forcing the rest of the system to rediscover intent, state, or operating rules. A bad boundary only redraws the diagram while the real coupling remains hidden underneath.</p>

<p>That matters even more in a field moving this quickly. Pipelines do take time to build and maintain, but the real cost comes from brittle, tightly coupled workflows that force full rewrites whenever a better tool arrives. If the interfaces are stable and the custom logic stays light, migrations remain local and often get faster too.</p>

<h2 id="aviation-solved-this-at-the-handoff">Aviation solved this at the handoff</h2>

<p>Part 1 pointed to the aviation version of this problem, but only in a narrow sense.</p>

<p>Commercial aviation is far more standardized than most software work, so the analogy is not that engineering can be formalized like pilot training. As <a href="https://mwalterskirchen.dev/blog/piloting-agentic-engineering/" title="Piloting Agentic Engineering - What Software Engineers Can Learn From The Aviation Industry">Maximilian Walterskirchen</a> points out, the useful lesson is narrower: autopilot did not remove pilots from the system. It changed the interface between automation, procedure, and human takeover. Aviation became safer because the system learned to make that handoff explicit.</p>

<p>Long-lived agentic systems need the same discipline. When a model changes, a tool fails, or a task falls outside the expected path, the workflow needs a stable takeover point rather than a hidden dependency on one giant prompt.</p>

<h2 id="real-systems-already-optimize-for-replaceable-interfaces">Real systems already optimize for replaceable interfaces</h2>

<table>
  <thead>
    <tr>
      <th>System example</th>
      <th>Stable interface</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>GitHub Copilot cloud agent</td>
      <td>Plan, branch diff, checks, and pull request are explicit handoff objects <a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/research-plan-iterate" title="Research, plan, and iterate on code changes with Copilot cloud agent - GitHub Docs">2</a></td>
    </tr>
    <tr>
      <td>Microsoft agent orchestration guidance</td>
      <td>Typed payloads, persisted state, evaluator loops, and human approvals keep stages replaceable <a href="https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns" title="AI agent orchestration patterns - Microsoft">4</a></td>
    </tr>
    <tr>
      <td>OpenAI’s agent guidance</td>
      <td>Start single-agent, then add specialization only when the task shape actually demands a new boundary <a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">5</a></td>
    </tr>
  </tbody>
</table>

<p>These systems do not scale by hiding more state inside the model. They scale by making interfaces explicit enough that one stage can evolve without collapsing the others.</p>

<h2 id="what-modularity-buys-you">What modularity buys you</h2>

<p>Modularity buys three things in practice:</p>

<ul>
  <li>Replaceability: one evaluator, planner, or model can change without rewriting the whole workflow.</li>
  <li>Evolvability: the system can adopt better tools gradually instead of through all-or-nothing rewrites.</li>
  <li>Long-term stability: humans can still inspect, resume, and correct the workflow because the state lives in artifacts and contracts.</li>
</ul>

<p>Automation makes modularity more valuable, not less.</p>

<h2 id="the-final-design-question">The final design question</h2>

<p>Once the boundaries are clear enough to survive change, the remaining problem is organizational.</p>

<p>Who plans, who executes, who checks, and where judgment stays human determines whether modularity becomes a real operating model.</p>

<p>👉 <a href="/2026/05/agentic-systems-7-designing-systems">Part 7: Designing agentic systems for engineering organizations</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[Long-lived agentic systems need modularity: explicit interfaces, stable handoffs, and local change boundaries that survive new tools, models, and teams.]]></summary></entry><entry><title type="html">Designing agentic systems for engineering organizations</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-7-designing-systems" rel="alternate" type="text/html" title="Designing agentic systems for engineering organizations" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-7-designing-systems</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-7-designing-systems"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><strong><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></strong> 👈</li>
    <li><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></li>
  </ol>
</div>

<p>The first post in this series opened with layoffs, regret, and rehiring. It also used autopilot as the precedent that matters: automation does not remove the system, it redistributes control inside it. That is the design problem for engineering organizations.</p>

<p>The goal is not to maximize agent count. It is to place automation where it reduces load without erasing the handoffs, validation, and judgment the organization still needs.</p>

<h2 id="from-diagnosis-to-operating-model">From diagnosis to operating model</h2>

<p>At scale, the unit of design is not the model. It is the workflow the model participates in.</p>

<p><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">OpenAI</a> frames agents as systems that accomplish tasks through tools and guardrails, not isolated prompts. <a href="https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns" title="AI agent orchestration patterns - Microsoft">Microsoft’s Azure guidance</a> and <a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/guidance/architecture/multi-agent-patterns" title="Multi-agent patterns - Microsoft">Copilot Studio’s multi-agent guidance</a> make the same point operationally: keep orchestration simple, validate typed payloads, persist state across stages, and require human approval for high-impact actions.</p>

<p>The same pattern already shows up in <a href="https://docs.github.com/en/copilot/concepts/agents/cloud-agent/about-cloud-agent" title="About GitHub Copilot cloud agent - GitHub Docs">GitHub Copilot’s cloud agent flow</a><a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/research-plan-iterate" title="Research, plan, and iterate on code changes with Copilot cloud agent - GitHub Docs">5</a>. Research happens first. A plan is formed. Work happens on a branch. Checks run. The diff gets reviewed. Only then does the pull request move forward.</p>

<h2 id="a-concrete-operating-model">A concrete operating model</h2>

<p>For a feature request that touches API behavior, UI text, tests, and rollout, a workable flow looks like this:</p>

<table>
  <thead>
    <tr>
      <th>Stage</th>
      <th>Primary actor</th>
      <th>Output</th>
      <th>Gate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Planning</td>
      <td>Human lead or planning agent</td>
      <td>Task packet with scope, constraints, and done definition</td>
      <td>Scope review</td>
    </tr>
    <tr>
      <td>Implementation</td>
      <td>Coding agent</td>
      <td>Branch diff plus changed-files manifest</td>
      <td>Tests and linters</td>
    </tr>
    <tr>
      <td>Evaluation</td>
      <td>Evaluator agent plus CI</td>
      <td>Eval bundle with results, risks, and policy checks</td>
      <td>Quality threshold</td>
    </tr>
    <tr>
      <td>Approval</td>
      <td>Human reviewer</td>
      <td>Pull request decision</td>
      <td>Merge approval</td>
    </tr>
    <tr>
      <td>Rollout</td>
      <td>Release automation plus human oversight</td>
      <td>Deployment state and rollback path</td>
      <td>Production gate</td>
    </tr>
  </tbody>
</table>

<p>That shape matters because each stage has a clear responsibility, a clear artifact, and a clear point where the next actor can say no.</p>

<p>It also does not mean the human only appears at final approval. In practice, the highest-value human involvement often happens earlier: on scope, design direction, risky changes, and the points where the system needs judgment rather than throughput.</p>

<p>The principles that follow are not abstract best practices. They are the places teams usually lose control once agents move past the demo stage.</p>

<h2 id="principle-0--structure-before-automation">Principle 0 — Structure before automation</h2>

<p>If the workflow is ambiguous, undocumented, or negotiated ad hoc in chat, agents will not fix it. They will execute the ambiguity faster. <a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">OpenAI’s guidance is blunt that “clear instructions reduce ambiguity”</a> and improve agent decision-making.</p>

<h2 id="principle-1--use-the-interfaces-the-organization-already-understands">Principle 1 — Use the interfaces the organization already understands</h2>

<p>Requirements, specs, design docs, diffs, test plans, and approval records already move work between humans in many organizations, even if they exist unevenly. Where those interfaces already exist, agent stages should plug into them rather than inventing a parallel AI-only process. Where they are still weak, AI increases the payoff of making them just explicit enough to support review and handoff. OpenAI explicitly recommends to <a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">“use existing documents”</a> when defining agent routines.</p>

<h2 id="principle-2--match-orchestration-to-the-shape-of-the-work">Principle 2 — Match orchestration to the shape of the work</h2>

<p>Use sequential flow for linear dependencies, specialization only when roles truly diverge, and maker-checker loops where quality is the main problem. Do not choose a pattern because it sounds advanced. Microsoft says to <a href="https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns" title="AI agent orchestration patterns - Microsoft">“use the lowest level of complexity that reliably meets your requirements”</a>.</p>

<h2 id="principle-3--separate-planning-execution-and-evaluation">Principle 3 — Separate planning, execution, and evaluation</h2>

<p>One actor can sometimes do more than one role, but the responsibilities should still be distinct. GitHub’s cloud agent flow deliberately separates <a href="https://docs.github.com/en/copilot/how-tos/use-copilot-agents/cloud-agent/research-plan-iterate" title="Research, plan, and iterate on code changes with Copilot cloud agent - GitHub Docs">“research a repository, create a plan, and make code changes on a branch”</a> before review. A system that cannot tell the difference between deciding, doing, and checking will drift.</p>

<h2 id="principle-4--put-validation-and-approval-inside-the-workflow">Principle 4 — Put validation and approval inside the workflow</h2>

<p>Deterministic checks and approval gates belong at the points where artifacts are produced and irreversible actions become possible. OpenAI argues that <a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building AI agents - OpenAI">high-risk actions should trigger human oversight</a>, and Microsoft goes further by saying to <a href="https://learn.microsoft.com/en-us/microsoft-copilot-studio/guidance/architecture/multi-agent-patterns" title="Multi-agent patterns - Microsoft">“require human approvals for high-impact cross-agent actions”</a>.</p>

<p>Those stage boundaries are also security boundaries. They let teams limit capabilities, inspect artifacts before granting more power, and avoid giving a context-exposed agent more write access than it needs.</p>

<h2 id="principle-5--optimize-the-real-constraint-not-the-busiest-stage">Principle 5 — Optimize the real constraint, not the busiest stage</h2>

<p>If review is slow, improve review. If validation is brittle, improve validation. If context transfer is failing, improve the artifact. More agents do not matter if the bottleneck still sits elsewhere. In many engineering teams, that bottleneck is PR review, which means the useful question is not “can AI replace review?” but “can better reviewer instructions, tighter diffs, stronger design context, or earlier evaluator feedback raise the signal of review?” Microsoft recommends <a href="https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns" title="AI agent orchestration patterns - Microsoft">tracking performance and resource usage metrics for each agent so that you can establish a baseline, find bottlenecks, and optimize</a>.</p>

<h2 id="principle-6--keep-humans-at-the-irreversible-edges">Principle 6 — Keep humans at the irreversible edges</h2>

<p>Aviation absorbed autopilot by redesigning supervision and takeover, not by pretending pilots were obsolete. Walterskirchen’s core warning is that <a href="https://mwalterskirchen.dev/blog/piloting-agentic-engineering/" title="Piloting Agentic Engineering - What Software Engineers Can Learn From The Aviation Industry">“pilots had been promoted from operators to supervisors of the machine”</a>. Engineering organizations should do the same with merges, production changes, policy decisions, and exception handling.</p>

<h2 id="principle-7--clarity-before-speed">Principle 7 — Clarity before speed</h2>

<p>Agentic systems don’t turn bad systems into good ones. They turn unclear systems into faster chaos. That is the lesson behind the layoffs in Part 1, the throughput limits in Part 3, and the autopilot precedent that runs through the series.</p>

<h2 id="what-durable-scale-actually-looks-like">What durable scale actually looks like</h2>

<p>Taken together, the lesson is narrow and unsentimental: agents belong inside engineering organizations as bounded contributors, not as replacements for the organization itself.</p>

<p>They can draft, implement, and evaluate, but they only create durable leverage when handoffs are clear, validation is built in, and humans remain accountable at the irreversible edges.</p>

<p>The systems that scale will not look like autonomous swarms. They will look like disciplined engineering teams with better tools.</p>

<p>The writing process ended up reinforcing the same point.</p>

<p>👉 <a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Part 8</a> is a postmortem on that process, and on why the writing only became good once it was treated as a system too.</p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><summary type="html"><![CDATA[A practical operating model for engineering teams using agents: separate planning, execution, evaluation, approval, and rollout with real gates.]]></summary></entry><entry><title type="html">Writing this series with AI: a postmortem</title><link href="https://blog.lavoie.sl/2026/05/agentic-systems-8-writing-with-ai-postmortem" rel="alternate" type="text/html" title="Writing this series with AI: a postmortem" /><published>2026-05-01T09:00:00-04:00</published><updated>2026-05-01T09:00:00-04:00</updated><id>https://blog.lavoie.sl/2026/05/agentic-systems-8-writing-with-ai-postmortem</id><content type="html" xml:base="https://blog.lavoie.sl/2026/05/agentic-systems-8-writing-with-ai-postmortem"><![CDATA[<div class="notice--primary">
  <p>This article is part of a series on agentic systems:</p>

  <ol>
    <li><a href="/2026/05/agentic-systems-1-struggling-to-scale">Agentic systems are struggling to scale (this should feel familiar)</a></li>
    <li><a href="/2026/05/agentic-systems-2-artisanal-era">Agentic systems are still in the artisanal era</a></li>
    <li><a href="/2026/05/agentic-systems-3-fundamental-limits">Agentic systems are bound by the same fundamental limits</a></li>
    <li><a href="/2026/05/agentic-systems-4-artifacts-are-compression">Artifacts are compression: how systems handle complexity</a></li>
    <li><a href="/2026/05/agentic-systems-5-why-all-systems-become-pipelines">Why all systems become pipelines</a></li>
    <li><a href="/2026/05/agentic-systems-6-long-lived-systems-need-modularity">Long-lived systems need modularity</a></li>
    <li><a href="/2026/05/agentic-systems-7-designing-systems">Designing agentic systems for engineering organizations</a></li>
    <li><strong><a href="/2026/05/agentic-systems-8-writing-with-ai-postmortem">Writing this series with AI: a postmortem</a></strong> 👈</li>
  </ol>
</div>

<p>This series was mostly written with AI assistance.</p>

<p>I want to say that plainly because pretending otherwise would be silly. What feels more worth saying is that the parts that work did not come from some perfect prompt. They came from turning a messy writing process into something more structured: plans, artifacts, review passes, explicit roles, and a lot of manual correction when the drafts drifted or sounded wrong.</p>

<p>That felt worth writing down. If the series has a useful point, I more or less learned it while writing it: AI was helpful, but only after I gave it a system to work inside.</p>

<h2 id="the-workflow">The workflow</h2>

<p>I did not get here by asking for seven finished essays and polishing the result. The process was much more iterative. <a href="https://www.microsoft.com/en-us/research/publication/prototypical-human-ai-collaboration-behaviors-from-llm-assisted-writing-in-the-wild/" title="Prototypical Human-AI Collaboration Behaviors from LLM-Assisted Writing in the Wild - Microsoft Research">Microsoft Research describes LLM-assisted writing as a process where users “actively refine, explore, and co-construct text”</a>, which feels close to what happened here.</p>

<table>
  <thead>
    <tr>
      <th>Phase</th>
      <th>Primary tool</th>
      <th>Output</th>
      <th>What mattered most</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Discovery</td>
      <td>ChatGPT voice chat</td>
      <td>Raw ideas, tensions, candidate directions</td>
      <td>Speed and breadth</td>
    </tr>
    <tr>
      <td>Architecture</td>
      <td>ChatGPT plus manual synthesis</td>
      <td>High-level plan, narrative spine, article map</td>
      <td>Coherence</td>
    </tr>
    <tr>
      <td>Evidence</td>
      <td>ChatGPT deep research plus manual triage</td>
      <td>Source list, prior art, examples</td>
      <td>Source quality</td>
    </tr>
    <tr>
      <td>Persistence</td>
      <td>Markdown repository</td>
      <td>Plans, research notes, article briefs</td>
      <td>Stable artifacts</td>
    </tr>
    <tr>
      <td>Drafting</td>
      <td>Copilot</td>
      <td>Initial article drafts</td>
      <td>Throughput</td>
    </tr>
    <tr>
      <td>Review</td>
      <td>Copilot plus manual critique</td>
      <td>Gap analysis, rewrite targets, defect patterns</td>
      <td>Judgment</td>
    </tr>
  </tbody>
</table>

<p>ChatGPT handled discovery, planning, and source gathering. Copilot handled the file-based drafting and review loops. My part was to keep cutting weak sources, fixing tone, and deciding what survived.</p>

<p>In practice the loop was simple:</p>

<ol>
  <li>Use ChatGPT to brainstorm, outline, and gather sources.</li>
  <li>Turn that into article plans and stable files in the repo.</li>
  <li>Use Copilot to draft and review against those files.</li>
  <li>Manually rewrite the parts that still felt weak, generic, or unsupported.</li>
</ol>

<p>That separation mattered more than the specific tools.</p>

<h2 id="what-actually-made-it-work">What actually made it work</h2>

<p>What made it work was structure, artifacts, and iteration.</p>

<p><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building agents - OpenAI">OpenAI argues that customers “typically achieve greater success with an incremental approach”</a>. That is a better description of the workflow than any story about fully autonomous generation.</p>

<p>Moving the work out of chat and into plans, briefs, and drafts made it easier to keep the argument stable. Once those artifacts existed, the models had something concrete to work against instead of a fading conversational context.</p>

<p>The useful skill ended up being learning how to inspect output, identify the failure mode, and redirect the next pass without reopening the whole problem.</p>

<p>Most of the quality still came from manual judgment: rejecting obscure sources, removing citation clutter, rewriting robotic transitions, and cutting sections that sounded tidy but dead. That is probably the part people underestimate most when they talk about AI-assisted writing.</p>

<h2 id="where-the-models-struggled">Where the models struggled</h2>

<p>The hardest failures were structural rather than grammatical or obviously hallucinatory.</p>

<p>The models were prone to soft argument drift: flattening distinctions, overexplaining points that had already been made, or turning a causal sequence into a list of adjacent observations. That showed up most clearly in endings and transitions, which often sounded like mini summaries instead of the next step in an argument.</p>

<p>They were also too willing to keep weak evidence if it fit the paragraph: obscure sources, repetitive links, or claims that sounded supported until the source was inspected closely.</p>

<p>And then there was the prose itself. It could look polished while still feeling generic. <a href="https://arxiv.org/abs/2603.20235" title="Writing literature reviews with AI: principles, hurdles and some lessons learned">The literature review paper on AI-assisted writing warns that “LLM outputs always appear at first glance to be well written, well informed and thought out, but closer reading reveals gaps, biases and lack of depth”</a>. That matches my experience almost exactly.</p>

<p>The uncomfortable part is that the workflow depended on me being able to notice those problems. If I could not tell when a source was weak or a section had subtly drifted, the process would have produced something smoother and worse.</p>

<h2 id="what-i-would-do-differently-next-time">What I would do differently next time</h2>

<p>The most useful part of the postmortem for me is what I would change next. The series got to a result that I like, but not efficiently.</p>

<h3 id="1-lock-the-constraints-and-artifacts-earlier">1. Lock the constraints and artifacts earlier</h3>

<p>I would define the thesis, source bar, stylistic anti-patterns, and review rubric much sooner. I would also create a claim map and source map for each article before drafting begins. A lot of the late-stage editing came from discovering those constraints too late.</p>

<h3 id="2-separate-review-and-measure-it">2. Separate review and measure it</h3>

<p>Too many prompts tried to fix everything at once. Next time I would separate factual review, structural review, prose review, and citation review. I would also track time per article, major rewrite passes, source replacements, and human editing time after the first draft. Without that, it is easy to say AI made the process faster and much harder to say where it actually did.</p>

<h3 id="3-encode-the-recurring-roles-into-the-system">3. Encode the recurring roles into the system</h3>

<p>I kept rediscovering the same roles in ad hoc prompts: series architect, source triage reviewer, article planner, structural critic, style enforcer, citation hygiene reviewer. Next time I would codify some of those into reusable skills or specialized agents with clearer contracts.</p>

<p><a href="https://openai.com/business/guides-and-resources/a-practical-guide-to-building-ai-agents/" title="A practical guide to building agents - OpenAI">OpenAI’s guidance is to “maximize a single agent’s capabilities first”</a>, so I would still start small. But <a href="https://docs.github.com/en/copilot/concepts/agents/cloud-agent/about-cloud-agent" title="About GitHub Copilot cloud agent - GitHub Docs">GitHub’s documentation notes that “you can create specialized custom agents for different tasks”</a>, and that seems like the right direction for work that keeps repeating the same evaluation patterns.</p>

<p><a href="https://docs.github.com/en/copilot/concepts/agents/cloud-agent/about-cloud-agent" title="About GitHub Copilot cloud agent - GitHub Docs">GitHub also puts this plainly: “The more Copilot cloud agent knows about the code in your repository, the tools you use, and your coding standards and practices, the more effective it will become”</a>. The writing equivalent is straightforward: the more the system knows about source quality, tone, formatting preferences, and the series spine, the less time gets wasted rediscovering them.</p>

<h2 id="the-process-proved-the-thesis">The process proved the thesis</h2>

<p>The series argued that agentic systems do not scale because of intelligence alone. They scale when work is decomposed into artifacts, pipelines, interfaces, and validation loops.</p>

<p>The writing process followed the same pattern. Publishable essays came from structured plans, stable artifacts, repeated review loops, and human intervention rather than one-shot generation.</p>

<p>The <a href="https://arxiv.org/abs/2603.20235" title="Writing literature reviews with AI: principles, hurdles and some lessons learned">literature review paper is even harsher on this point, warning that “a press-button strategy leaving AI to do the work is a recipe for disaster”</a>. That is exactly right. The more autonomous the drafting looked, the more human the editing had to become.</p>

<p>This also speaks about the immaturity of my writing process. I managed to get by because it was only a few articles, but it would definitely not scale to something like a multi-chapter book without the improvements above.</p>

<p>So if there is one lesson I would keep from this process, it is this: AI did not replace the writing system. It made the writing system more necessary.</p>]]></content><author><name>Sebastien Lavoie</name></author><category term="AI" /><category term="Agentic Systems" /><category term="Software Engineering" /><category term="Writing" /><category term="Series" /><summary type="html"><![CDATA[A postmortem on writing this series with AI: what structure, artifacts, review loops, and manual judgment actually contributed, and where the models failed.]]></summary></entry><entry><title type="html">Override Doctrine ODM mapping for Symfony FOSUserBundle</title><link href="https://blog.lavoie.sl/2015/01/symfony-override-doctrine-mapping" rel="alternate" type="text/html" title="Override Doctrine ODM mapping for Symfony FOSUserBundle" /><published>2015-01-06T13:44:00-05:00</published><updated>2015-01-06T13:44:00-05:00</updated><id>https://blog.lavoie.sl/2015/01/symfony-override-doctrine-mapping</id><content type="html" xml:base="https://blog.lavoie.sl/2015/01/symfony-override-doctrine-mapping"><![CDATA[<p>In Symfony Cookbook <a href="http://symfony.com/doc/current/cookbook/bundles/override.html#entities-entity-mapping">How to Override any Part of a Bundle</a>, it is written that you cannot override entity mappings and only attributes can be modified in superclasses. However, it is possible to hack you way through and <a href="http://symfony.com/doc/current/bundles/DoctrineMongoDBBundle/index.html#registering-event-listeners-and-subscribers">register an Event Listener</a> on loadClassMetadata that will rewrite the mapping on-the-fly. I would not qualify this as a good approach, but it is the only way I found.</p>

<p>A similar solution can be used for Doctrine ORM.</p>

<p>Here is an example, removing the uniqueness on emailCanonical of FOSUserBundle:</p>

<figure class="highlight"><pre><code class="language-php" data-lang="php"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="code"><pre><span class="cp">&lt;?php</span>
<span class="c1"># ClassMetadataListener.php</span>

<span class="kn">namespace</span> <span class="nn">Acme\UserBundle\EventListener</span><span class="p">;</span>

<span class="kn">use</span> <span class="nc">Doctrine\ODM\MongoDB\Event\LoadClassMetadataEventArgs</span><span class="p">;</span>

<span class="cd">/**
 * Ran when Mongo metadata is loaded.
 */</span>
<span class="kd">class</span> <span class="nc">ClassMetadataListener</span>
<span class="p">{</span>
    <span class="k">public</span> <span class="k">function</span> <span class="n">loadClassMetadata</span><span class="p">(</span><span class="kt">LoadClassMetadataEventArgs</span> <span class="nv">$eventArgs</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="nv">$classMetadata</span> <span class="o">=</span> <span class="nv">$eventArgs</span><span class="o">-&gt;</span><span class="nf">getClassMetadata</span><span class="p">();</span>

        <span class="c1">// Override FOS to not have unique emails</span>
        <span class="k">if</span> <span class="p">(</span><span class="nv">$classMetadata</span><span class="o">-&gt;</span><span class="n">reflClass</span><span class="o">-&gt;</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'FOS\UserBundle\Model\User'</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">foreach</span> <span class="p">(</span><span class="nv">$classMetadata</span><span class="o">-&gt;</span><span class="n">indexes</span> <span class="k">as</span> <span class="nv">$i</span> <span class="o">=&gt;</span> <span class="nv">$index</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="nb">count</span><span class="p">(</span><span class="nv">$index</span><span class="p">[</span><span class="s1">'keys'</span><span class="p">])</span> <span class="o">===</span> <span class="mi">1</span> <span class="o">&amp;&amp;</span> <span class="k">isset</span><span class="p">(</span><span class="nv">$index</span><span class="p">[</span><span class="s1">'keys'</span><span class="p">][</span><span class="s1">'emailCanonical'</span><span class="p">]))</span> <span class="p">{</span>
                    <span class="nv">$classMetadata</span><span class="o">-&gt;</span><span class="n">indexes</span><span class="p">[</span><span class="nv">$i</span><span class="p">][</span><span class="s1">'options'</span><span class="p">][</span><span class="s1">'unique'</span><span class="p">]</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
                    <span class="k">break</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="c1"># services.yml</span>
<span class="na">services</span><span class="pi">:</span>
    <span class="na">acme.user.metadata_listener</span><span class="pi">:</span>
        <span class="na">class</span><span class="pi">:</span> <span class="s">Acme\UserBundle\EventListener\ClassMetadataListener</span>
        <span class="na">tags</span><span class="pi">:</span>
            <span class="pi">-</span>  <span class="pi">{</span> <span class="nv">name</span><span class="pi">:</span> <span class="nv">doctrine_mongodb.odm.event_listener</span><span class="pi">,</span> <span class="nv">event</span><span class="pi">:</span> <span class="nv">loadClassMetadata</span> <span class="pi">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p><a href="https://gist.github.com/lavoiesl/0ac2d841b07ea122bfd0">View Gist</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="FOSUserBundle" /><category term="Mongo" /><category term="Symfony2" /><category term="Doctrine" /><summary type="html"><![CDATA[In Symfony Cookbook How to Override any Part of a Bundle, it is written that you cannot override entity mappings and only attributes can be modified in superclasses. However, it is possible to hack you way through and register an Event Listener on loadClassMetadata that will rewrite the mapping on-the-fly. I would not qualify this as a good approach, but it is the only way I found.]]></summary></entry><entry><title type="html">Preventing cache stampede when using Doctrine Cache</title><link href="https://blog.lavoie.sl/2014/12/preventing-cache-stampede-using-doctrine-cache" rel="alternate" type="text/html" title="Preventing cache stampede when using Doctrine Cache" /><published>2014-12-04T11:28:00-05:00</published><updated>2014-12-04T11:28:00-05:00</updated><id>https://blog.lavoie.sl/2014/12/preventing-cache-stampede-using-doctrine-cache</id><content type="html" xml:base="https://blog.lavoie.sl/2014/12/preventing-cache-stampede-using-doctrine-cache"><![CDATA[<p><a href="http://en.wikipedia.org/wiki/Cache_stampede">Wikipedia has short and clear article</a> on the matter, cache stampede can be quite deadly, especially when you are rebooting your server, clearing your cache or having a midnight maintenance (cronjob).</p>

<p>Cache stampede, put simply, is when your process is trying to fetch an expensive cache entry, it is not there, and tries to build it, but a simultaneous process comes and does the same thing. This results in resource waste because of processes all trying to do the same thing, uselessly.</p>

<p>The trick is to put a lock on the cache entry and make the other processes wait. I use the trick of having a TTL for the cache calculation. This way, even if the server crashes or there is an exception, the lock will expire and another process will take over. This estimation is also used to calculate how much time to sleep while waiting.</p>

<p>This example uses Doctrine Cache as a global variable, but this should definitely be refactored (or change to another system).</p>

<figure class="highlight"><pre><code class="language-php" data-lang="php"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
</pre></td><td class="code"><pre><span class="cp">&lt;?php</span>
<span class="c1"># cacheGetDefault.php</span>

<span class="cd">/**
 * Get a value from the cache or compute it using a callback
 * Includes cache stampede protection
 * @link http://en.wikipedia.org/wiki/Cache_stampede
 * @link http://blog.lavoie.sl/2014/12/preventing-cache-stampede-using-doctrine-cache
 * @global cache Doctrine\Common\Cache\Cache
 *
 * @param  string  $key         Cache key
 * @param  Closure $callback    Computing function
 * @param  float   $ttl         Seconds
 * @param  float   $stampedeTtl Seconds before cache stampede expires
 *                              Should be about 2-3 times the estimated time to build the cache
 * @return mixed
 */</span>
<span class="k">function</span> <span class="n">cacheGetDefault</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="kt">Closure</span> <span class="nv">$callback</span><span class="p">,</span> <span class="nv">$ttl</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="nv">$stampedeTtl</span> <span class="o">=</span> <span class="kc">null</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">global</span> <span class="nv">$cache</span><span class="p">;</span>

    <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$cache</span><span class="o">-&gt;</span><span class="nf">fetch</span><span class="p">(</span><span class="nv">$key</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="kc">false</span> <span class="o">!==</span> <span class="nv">$data</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nv">$data</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="kc">null</span> <span class="o">!==</span> <span class="nv">$stampede</span><span class="p">)</span> <span class="p">{</span>
        <span class="nv">$stampede</span> <span class="o">=</span> <span class="nb">max</span><span class="p">((</span><span class="n">double</span><span class="p">)</span> <span class="nv">$stampede</span><span class="p">,</span> <span class="mf">0.001</span><span class="p">);</span> <span class="c1">// prevention for infinite ttl</span>
        <span class="c1">// data is being computed, wait</span>
        <span class="k">while</span> <span class="p">(</span><span class="s1">'__STAMPEDE__'</span> <span class="o">===</span> <span class="nv">$data</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// wait 1/20th of the stampede TTL, to give the CPU a chance</span>
            <span class="nb">sleep</span><span class="p">(</span><span class="nv">$stampede</span> <span class="o">/</span> <span class="mi">20</span><span class="p">);</span>
            <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$cache</span><span class="o">-&gt;</span><span class="nf">fetch</span><span class="p">(</span><span class="nv">$key</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">if</span> <span class="p">(</span><span class="nv">$data</span> <span class="o">===</span> <span class="kc">false</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="kc">null</span> <span class="o">!==</span> <span class="nv">$stampedeTtl</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// acquire lock</span>
            <span class="nv">$cache</span><span class="o">-&gt;</span><span class="nf">save</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="s1">'__STAMPEDE__'</span><span class="p">,</span> <span class="nv">$stampedeTtl</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="c1">// compute the actual data</span>
        <span class="nv">$data</span> <span class="o">=</span> <span class="nv">$callback</span><span class="p">();</span>
    <span class="p">}</span>

    <span class="nv">$cache</span><span class="o">-&gt;</span><span class="nf">save</span><span class="p">(</span><span class="nv">$key</span><span class="p">,</span> <span class="nv">$data</span><span class="p">,</span> <span class="nv">$ttl</span><span class="p">);</span>

    <span class="k">return</span> <span class="nv">$data</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></figure>

<figure class="highlight"><pre><code class="language-php" data-lang="php"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="cp">&lt;?php</span>
<span class="c1"># useCache.php</span>

<span class="nv">$cacheTtl</span> <span class="o">=</span> <span class="mi">3600</span><span class="p">;</span> <span class="c1">// an hour</span>
<span class="nv">$stampedeTtl</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// seconds before releasing lock</span>

<span class="nf">cacheGetDefault</span><span class="p">(</span><span class="s1">'recent_tweets'</span><span class="p">,</span> <span class="k">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="k">return</span> <span class="nb">file_get_contents</span><span class="p">(</span><span class="s1">'https://api.twitter.com/1.1/search/tweets.json?q=@lavoiesl'</span><span class="p">);</span>
<span class="p">},</span> <span class="nv">$cacheTtl</span><span class="p">,</span> <span class="nv">$stampedeTtl</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p><a href="https://gist.github.com/lavoiesl/9cdc5893a71c53e16b47">View Gist</a></p>]]></content><author><name>Sebastien Lavoie</name></author><category term="Caching" /><category term="PHP" /><category term="Doctrine" /><category term="Performance" /><summary type="html"><![CDATA[Wikipedia has short and clear article on the matter, cache stampede can be quite deadly, especially when you are rebooting your server, clearing your cache or having a midnight maintenance (cronjob).]]></summary></entry></feed>