Engineering
Backpressure: The Pattern That Separates Systems That Work From Systems That Flop
Backpressure is the simplest reliability pattern most teams skip. When an AI coding agent generates PRs faster than humans can review them, or a payment service accepts requests faster than the database can commit — backpressure is what says no before everything falls over. Here's how to apply it across code review, API design, and CI pipelines.
There are two ways to handle a mismatch between how fast work is produced and how fast it can be consumed. The first is to let the producer run at full speed and hope the consumer keeps up. The second is to make the producer slow down to match the consumer's pace. The first approach is called "things breaking at 2 a.m." The second is called backpressure.
Backpressure is a pattern borrowed from fluid dynamics and queuing theory, applied to software systems for decades, and suddenly relevant again because AI coding agents produce code orders of magnitude faster than humans can review it. But the pattern applies far beyond AI — it's the difference between a payment gateway that degrades gracefully under load and one that corrupts your ledger when traffic spikes.
What Backpressure Actually Is
In a system where component A feeds data to component B, backpressure is B's ability to tell A "slow down" or "stop" when B is saturated. The signal travels upstream. The producer — not the consumer — bears the cost of adjusting.
Without backpressure, the producer generates work at will. The consumer either absorbs the mismatch, falls behind, breaks under load, or cuts corners to keep up. All four outcomes are bad. With backpressure, the consumer refuses work it can't handle. The system as a whole degrades gracefully instead of failing catastrophically.
Lucas Costa's essay "Backpressure Is All You Need," trending on Hacker News this week, reframes this pattern for the AI era: "Whenever there's no backpressure, the producer is free to generate work at will, and the consumer is forced to absorb the mismatch." His argument applies the pattern to a problem every engineering team is about to face — LLMs writing code faster than any human can review it.
Backpressure in CI/CD: The Original Gate
The most familiar form of backpressure in software is a CI pipeline that refuses to merge code until tests pass. You don't submit a pull request with failing tests (or you shouldn't). A reviewer shouldn't even look at a PR until CI is green. The test suite functions as backpressure: the machine says "no" before a human has to.
Before CI/CD was universal, the review bottleneck was human attention. A senior engineer would spend hours tracing through code looking for type mismatches, broken imports, and logic errors that a compiler or test runner could catch in seconds. CI automated the gatekeeping. Now the human reviews design, readability, and architecture — the things machines can't check — while the machine handles the things humans are slow and error-prone at.
TypeScript is backpressure. When you pass a string to a function that expects a number, the compiler refuses to compile. The mismatch is caught at the boundary where it was introduced, not six layers deep in production when a user clicks a button. Before TypeScript, the only way to catch that bug was for a reviewer to follow the prop, follow the callback, check the caller, check the caller's caller, and hope the mismatch was visible in the diff.
| Stage | Without Backpressure | With Backpressure | Mechanism |
|---|---|---|---|
| Development | Code compiles, ships with type errors caught in production | Compiler refuses invalid code at write time | TypeScript strict mode, Rust borrow checker |
| Pre-commit | Linting violations make it to PR review; reviewer catches them | Pre-commit hooks block commits with lint errors | Husky + ESLint, pre-commit, ruff |
| PR Review | Reviewer spends 40% of time catching mechanical issues | CI runs tests, types, linting before human looks at diff | GitHub Actions, GitLab CI |
| Merge | Code with failing tests can be merged | Branch protection blocks merge if CI is red | GitHub branch protection rules |
| Deploy | Broken deploy goes to 100% of traffic | Canary deploy with automatic rollback on error rate spike | Argo Rollouts, feature flags, circuit breakers |
| Runtime | One slow service degrades all dependent services | Circuit breaker opens, dependent services degrade gracefully | Resilience4j, Polly, Hystrix |
The AI Agent Problem: Production at Review Speed
Here's the new problem. A human developer produces one or two substantive pull requests per day. A reviewer can keep up with that pace. An AI coding agent — Claude Code, Codex CLI, Cursor Agent — can produce five to ten PRs in an afternoon.
You have three options:
Let the agent run unattended and hope the repository survives. Fast, exciting, and stupid. Leads to bugs, confused changes, and a flood of PRs humans can't review quickly enough. Eventually, standards drop and someone merges code they don't understand.
Force a human to review every tiny step. Safe, but slow enough to defeat the purpose of using an agent. If you're still steering every minor decision, you haven't delegated anything meaningful.
Add backpressure between the agent and the reviewer. Make the agent validate its own work before a human sees it. The machine says "no" before the human has to.
Here's what that looks like in practice:
Layer 1: Automated Tests (The Agent Must Prove Correctness)
The agent doesn't submit a PR until its own tests pass. More importantly, it doesn't submit a PR until it has written tests for the code it produced. The backpressure rule: no PR without passing tests that the agent itself wrote.
OpenCode CLI and Claude Code both support this workflow. Configure your agent to run the full test suite before opening a PR. If tests fail, the agent fixes them. No human reviews a red build.
Layer 2: Type Checking (The Compiler as Gate)
TypeScript strict: true is backpressure for LLM-generated code. An agent that writes any-typed code or mismatched prop shapes gets blocked at compile time. The machine catches the error at the boundary. The reviewer never sees it.
This is why teams adopting AI coding agents should invest in stricter type systems, not looser ones. A permissive type system means the agent's mistakes flow through to review. A strict one means the agent confronts its own errors.
Layer 3: Linting and Formatting (Style as Enforced Contract)
Pre-commit hooks running ESLint, Prettier, ruff, or biome block commits that violate style rules. An AI agent that generates inconsistently formatted code simply can't commit it. The machine enforces the contract.
Layer 4: Review Bots (Machine Review Before Human Review)
A second AI model reviews the first AI's code before a human sees it. This sounds circular — using AI to check AI — but it catches the mechanical errors humans are bad at: dead code, missing error handling, inconsistent naming, untested edge cases.
The workflow: coding agent generates code → review bot runs automated checks → coding agent fixes flagged issues → review bot re-checks → human reviews only what passed both filters. The human's attention is reserved for architecture, design, and domain correctness — the things machines are bad at.
The irony Costa points out: without this pipeline, the human becomes "an expensive clipboard doing the mechanical work between two machines."
Backpressure in Production Systems
The pattern applies equally to runtime systems. Here are the mechanisms every production service should have:
Rate Limiting
A service that accepts unlimited requests will eventually fall over. Rate limiting is backpressure: "you've sent N requests in the current window; the next one gets a 429."
For APIs, use token-bucket or sliding-window algorithms. In Next.js API routes, next-rate-limiter or a Redis-backed counter handles this in under 20 lines of code. In Express, express-rate-limit. The key: rate limit at the edge (reverse proxy or API gateway), not deep in application code.
Circuit Breakers
When a downstream service is failing, a circuit breaker stops sending it requests. Instead of every request timing out after 30 seconds (and tying up your connection pool), the circuit breaker fails fast with a 503. The downstream service gets breathing room. Your service stays responsive for everything else.
Three states: closed (requests flow normally), open (requests fail immediately), half-open (a test request probes whether the downstream recovered). Implement with Resilience4j (Java), Polly (.NET), or a simple wrapper in any language — the logic fits in 50 lines.
Load Shedding
When a server is overloaded, it should reject low-priority work to protect high-priority work. A health-check endpoint should never return 503 because the server is busy generating reports. Load shedding drops the reports, keeps the health check alive, and the load balancer doesn't pull the server out of rotation.
Where Teams Get This Wrong
The most common failure mode isn't technical — it's organizational. Teams add backpressure mechanisms, then override them when they're inconvenient.
- The CI pipeline blocks a merge because tests are flaky. Someone disables the flaky test instead of fixing it.
- The rate limiter blocks a legitimate customer during a sale. Someone bumps the limit instead of investigating the traffic pattern.
- The type checker complains about a complex generic. Someone slaps
// @ts-ignoreon it.
Backpressure only works if the team treats the "no" as a signal worth investigating, not an obstacle to route around. Every time you override a backpressure mechanism, you should ask: "am I fixing a false positive, or am I lowering the bar?"
What to Do Tomorrow
If your team is adopting AI-assisted development or just trying to keep production stable, start with these three changes:
Block PR submission until CI is green. This is table stakes, but many teams still allow it. GitHub branch protection rules take five minutes to configure. A PR with a red build should not be reviewable.
Add a pre-commit hook that runs linting. If
eslint --fixorruff check --fixcan fix it, don't let a human spend time on it. Don't let an AI agent generate it.Pick one service and add a circuit breaker to its most critical downstream dependency. Not all of them. One. Watch what happens the next time that dependency has an incident. If your monitoring shows the circuit breaker opening and your service staying up, you've proved the pattern works. Then roll it out to the rest.
Backpressure is not complicated. It's discipline. The machine says "no" so the human can focus on what the machine can't do. In an era where machines are increasingly doing the producing, that distinction matters more than ever.
Further reading: Lucas Costa, "Backpressure Is All You Need" (May 29, 2026); Michael Nygard, "Release It!" (Pragmatic Bookshelf, 2nd ed.); Netflix Tech Blog on Hystrix and circuit breaker patterns.
Tags
- backpressure
- systems-design
- reliability
- ai-agents
- code-review
- ci-cd
More on engineering
- Connection Pooling Is Not Optional — PostgreSQL at Scale for Multi-Tenant SaaSEvery Rails/Django/Node.js tutorial ships with a database.yml that opens 5 connections. Multi-tenant SaaS at 200 tenants means 1,000 connections. PostgreSQL falls over around 300. Here's how connection pooling — specifically pgbouncer — prevents the crash you're heading toward.
- MAI-Code-1-Flash — Microsoft Ships Seven Coding Models, One Worth Paying Attention ToMicrosoft dropped MAI-Code-1-Flash alongside six other MAI models. It's fast, MIT-licensed, and competitive with closed-source alternatives on coding benchmarks. Here's what Indian dev teams should know before reaching for it.
- What Stanford CS336 Teaches About AI Agent Reliability — And What It Doesn'tStanford's CS336 course published AI agent guidelines that went viral on HN this week. The document is written for teaching assistants, not production engineers, but its principles map directly to building reliable agent systems. Here are the rules that translate — and the production gaps they leave open.