The Retro That Blamed the Wrong Team — And What It Taught Me About Agent Swarms
Backpressure: The Control Signal Missing From Your Sprint Board And Your Agent Fleet
Your board looks fast. Five features in flight, everyone busy, standup sounds great. Then review backs up four days and nobody can explain why. Fire fifteen subagents at once and you get the same shape of failure — half of them come back with garbled context and nobody can explain that either. Same root cause both times: nothing in the system ever said "slow down."
The retro that blamed reviewers
A team I worked with ran a retro after a rough sprint. The complaint: "reviews are too slow, we need another reviewer." I pulled up the board. Seven PRs sitting in review, three of them four days old. But the real story wasn't in review — it was upstream. That sprint the team had started five features in parallel instead of their usual two, because "we had the capacity." Nobody capped work-in-progress. Review didn't get slower. Review got a queue dumped on it that used to trickle in one at a time. The bug wasn't the reviewers. It was that nothing upstream ever said "stop starting, start finishing."
Name the concept: backpressure
Backpressure is a systems term, borrowed from fluid dynamics and formalized in networking (TCP flow control) and streaming systems (Reactive Streams' explicit backpressure protocol, Kafka's pull-based consumer model). It means: when a consumer is slower than a producer, the consumer sends a signal back upstream — "I'm full, stop sending" — and the producer respects it.
The critical property is that backpressure is a signal, not a symptom. TCP doesn't let a fast sender flood a slow receiver into silent packet loss — it shrinks the sender's window and makes the slowdown explicit on both ends. That's the opposite of what happens in a system with no backpressure: the producer keeps producing, the queue keeps growing somewhere you're not watching, and nothing degrades loudly. It degrades silently, until something downstream — a buffer, a reviewer, a context window — blows past capacity and fails ugly.
- ▹With backpressure: the producer slows down, the queue stays bounded, and the system tells you it's at capacity.
- ▹Without backpressure: the producer keeps going, the queue grows invisibly, and the system tells you nothing — until it can't.
Little's Law in one breath
Little's Law is the queueing-theory identity that makes this concrete: WIP = Throughput × Latency. Work-in-progress equals your rate of completion times how long each item sits in the system. Flip it around and "sprint velocity" stops being a virtue and becomes an output: Throughput = WIP / Latency. If you want more throughput without cutting latency to zero — and you can't — the only lever that isn't magic is capping WIP on purpose and watching latency respond. A team that adds parallel work without touching this equation isn't increasing throughput. It's increasing WIP, and by the same math, increasing latency for everything already in flight.
WIP = Throughput × Latency
Want more Throughput?
→ Either cut Latency per item (rare, needs real capacity work)
→ Or you're just inflating WIP and calling it velocityThe board is a queue
A Kanban board is a chain of queues: To Do → In Progress → Review → QA → Done. Each column has a real, if unstated, service rate. Add parallel workstreams — more features started at once — without capping WIP per column, and you haven't added capacity to the system. You've added load to the front of the pipe while the narrowest downstream stage, usually review or QA, because it needs a human and full attention, keeps running at exactly the same speed it always did. The backlog doesn't disappear. It relocates to the stage with the least visibility and the least slack, and it sits there aging until someone notices in a retro and blames the wrong stage.
The bridge: the same failure shape in multi-agent orchestration
This is the part that makes backpressure a 2026 concept, not a 2015 one. Swap "sprint board" for "orchestrator dispatching subagents" and the failure is structurally identical. An orchestrator that fires N subagents the moment a task list exists — no cap, no completion-aware throttle — is an unbounded producer against consumers that are all rate-limited: a shared context window, a CI runner pool, an LLM provider's concurrent-request ceiling, a shared file system checkpoint. It doesn't get N× the work done. It gets rate-limited responses, truncated context, flaky CI runs competing for the same runners, agents stepping on each other's file edits — and because none of that shows up as one loud error, it reads as noise instead of what it is: a capacity problem.
The fix is the same shape twice
On the board: don't add reviewers, add a WIP limit. Cap in-progress items per column — two per engineer is a common starting point — and force the team to finish before starting. The queue that used to pile up invisibly in review now shows up as a blocked "To Do" column: visible, boring, and easy to act on.
In the orchestrator: add a credit- or ack-style throttle to agent dispatch, the same shape as TCP's sliding window. The orchestrator holds a fixed number of "dispatch credits" — concurrent subagents allowed — and returns a credit only when an agent completes and its result is consumed, not just when it's launched. Your orchestration framework's concurrency controls (LangGraph's concurrency limits, for instance) or a plain semaphore around your subagent spawner implement the same idea. An orchestrator that waits for an ack before issuing the next credit isn't slower. It's the one whose failures are visible — "queue full, waiting" — instead of silent: garbled context, half-finished PRs from three agents that collided on the same file.
// TCP-style shape, applied to agent dispatch
credits = MAX_CONCURRENT_AGENTS
for task in backlog:
wait_until credits > 0 // this line IS the backpressure
credits -= 1
dispatch(task, on_complete=lambda: credits += 1)Closing beat
"Queue full" should be a state you design for, not a failure you discover in a retro. On the board, a visible WIP-limit block is a good day, not a bad one — it means the signal fired before the queue moved somewhere dark. In the orchestrator, "waiting for a dispatch credit" is a healthy log line, not a bug report. The EM capping WIP on a Kanban board and the engineer capping concurrent subagents in an orchestrator are doing the identical job: making the system say "slow down" out loud, before it says nothing at all and breaks quietly downstream.
Extend your knowledge
- ▹Read up on TCP's flow-control / sliding-window mechanism in any solid networking reference — it's the cleanest, most literal implementation of backpressure, and it maps almost line-for-line onto agent dispatch throttling.
- ▹Check how your CI provider (GitHub Actions, GitLab CI) exposes concurrency limits per runner group. That's a hard, enforced backpressure boundary your orchestrator is racing against whether you've modeled it or not.
- ▹If you run agents through LangGraph or another orchestration framework, check its concurrency/queue settings. Most give you a credit-style cap for free — the failure mode is leaving it unset, not the tooling lacking it.
- ▹On the team side, instrument WIP-per-column on your actual board for two sprints before changing anything. Little's Law only becomes actionable once you can see WIP and latency as numbers, not vibes.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.