AI-friendly architecture: write better software, not just more of it
The story everyone tells about AI engineering is volume: more code, faster — until it collapses into unmaintainable slop. The real story is the opposite. AI enables building much better software.
AI made one thing radically cheap: deep, local, isolated implementation. A heavily optimised, frontier-grade subsystem — the kind that used to take the best teams months — is now plausibly a couple of days' work when the boundary around it is clean. Meanwhile the best models can barely move a button inside a large, stateful, interconnected app. If your AI is writing slop, the problem is probably your architecture.
The claim of this essay is that AI-led development has elevated isolation to the single most important concern in software architecture: the models make isolated, deterministic, low-state code nearly free to write and maintain — and make everything coupled brutally expensive to touch.
The cost inversion
Eighteen months ago, implementation depth was the dominant cost in software. A 5,000-line numerical optimisation took months to write and was heavy to review and maintain. A shared mutable field felt free — one line. A pub/sub event felt efficient — one emitter instead of a thousand lines of plumbing.
AI inverted that. An isolated subsystem is basically free to build and maintain. The critical cost became the burden the code puts on the next entity that touches it. A human carries an implicit map of the codebase and can filter out the ambient state that doesn't bear on the change at hand. An agent can't. Every time it moves a button it reconstructs the local constraints from scratch — the norms, the invariants, what this call means at this lifecycle edge. Review stops being a gate cleared once at merge and becomes a cost paid on every touch. And coupling sets the size of that cost: in a coupled system, touching a button loads the whole web the button is snarled into.
This follows from how the models reason. LLM reasoning is intensely linear. "Big context" duped us — context was never bound attention. A model can follow one isolated path almost forever: a linear A → B → C → D signal is strong and cheap, and every step focuses the model's attention squarely on the next. But it struggles with a stateful, temporal web: which subscriber saw this flag? which lifecycle edge changed what a call means? which state combination triggers the reported bug? The answer is somewhere, but nothing local says where to look — "many things may affect this, trace them all one by one" is a weak signal to land on and an expensive path to walk once landed.
The obvious reply is that the next model dissolves this. The labs have thrown everything at it — extended thinking, prompt caching, million-token windows, repo-wide retrieval — and the brute force kind of works, until it doesn't. Spending tokens following the slow paths buys headroom, not immunity. Past a certain density, you pay the tokens and the model still drops a constraint.
The other standard answer is tests and harnesses. Necessary, not sufficient. On a coupled, stateful codebase the harness doesn't dissolve the global problem — the agent still has to work through it, and if everything is connected, every change breaks tests. Triaging real reds from wanted changes is more work and more error-prone than coding without tests. In other words: sufficiently bad architecture makes tests stop being regression guards and become progression guards.
Architecture for AIs: narrow entrances, long tunnels
For a system coded mostly by LLMs, good architecture is whatever makes problems local. This one concern overwhelms everything else. The shape I keep returning to: narrow entrances, long tunnels.
A narrow entrance is a small, explicit contract — ideally a pure, stateless function: visible inputs, visible outputs, as few options as possible, ideally none. A long tunnel is everything behind it. The system only ever sees the entrance. And that's what unlocks complexity: the innards can be as gnarly as the domain forces, so long as they're isolated. The poison spreads only when state leaks outward.
The thing we underprice most is the pure function. You can pack an absurd amount of sophistication inside one with almost no error space, because the errors are bounded to the inputs and outputs. Lock the entrance with a correctness test and a performance benchmark, and an agent can work the tunnel overnight — refactor, optimise, try alternative implementations, hunt numerical edge cases — against a sandbox that proves every change. This is where a linear reasoner comes alive: a bounded error space, a deep isolated path, and a number that says better or worse. The optimisation becomes nearly free.
Take a spreadsheet solver running cold — the example, like everything here, comes from building Purple Hammer1. It's a long pipeline — dependency graph, parse, AST, compress, pack, solve, unpack — and nearly every junction forks: scalar or SIMD, serial or parallel, CPU or GPU, how to shard, what to cache. The optimal combination is genuinely shape- and hardware-dependent, and the speedups interact: a technique that wins above a certain size loses below it, and turning one knob moves the right setting for the next. The ideal isn't twenty sophisticated instruments; it's a reactive configuration machine balancing all those interdependencies — months or years of human work. To an orchestrator sitting above the models, that entire challenge devolves into a gradient-descent-shaped problem they regress against.
And you can get away with it because the barrier is near enough absolute. An agent outside Solve {cells} needs nothing from the inside. An agent working inside Solve {cells} needs nothing from the outside.
You can't buy any of that on a connected system. A 10,000-line optimisation behind a three-argument pure function is cheap the moment it ships — the system doesn't care how deep the tunnel runs, only whether the entrance obeys its contract. A one-line shared mutable field can be ruinous, because it couples behaviour across systems. Deep isolated code got cheap; connection got expensive; the old instinct had it backwards.
Pub/sub is the sharpest illustration. The problem isn't events; it's the opaque pipeline between the emit and the outcome. "Emit here, and somewhere something reacts, in some order, depending on what's subscribed right now" stops being enumerable as the system grows — and the reasoning the agent can't do is exactly the temporal sweep over that hidden web. The boring alternative is the strong one: deterministic flow over explicit data, direct calls, immutable snapshots, async edges only where the outside world forces them, hard cores sealed behind pure interfaces.
The hard part is isolating those cores from concerns that are naturally global. Take spreadsheet locale — a number is either "1,000.5" or "1.000,5", the separator "," or ";". The reflex lets it leak: a global locale flag plus a locale option on every parse and format, until every computation has to know about locale. The fix costs more plumbing and leaks far less: one canonical representation internally, with a single convert(from, to, payload) at every read and write. The core never sees a locale, and convert — a stateless function against a frozen lawbook — can't really regress. The whole sprawling concern collapses to one live question: was this value converted, at the right boundary?
It's the same move every time. The pure function, the convert lawbook, the snapshot in place of the event — each collapses "account for everything that might bear on this" into a rule the model can read off the code in front of it. The answer stops being somewhere in the system; it's here.
None of this is new. Pure functions and low coupling have been preached for fifty years. What changed is the magnitude. Two years ago, a strict, specialised class was a two-week project enterprises mandated and startups laughed at before dropping in a global — and for a human team the shortcut really was the fast path; the rigorous boundary really was a tax. AI inverts that calculation: the boundary is nearly free now, so the tax is gone. If the models change, the advice can change with them — but low coupling was good long before any of this, so it costs nothing to be wrong about them.
Better is faster too
We could be building much better software than we are. Eighteen months ago, a great company — the kind that got funded on technicals — had maybe one or two breakthrough engineering innovations near the frontier. Pay the price of proper AI-friendly architecture and you can ship them every Monday — the benchmarks in 1 are two of ours.
And it's not just the hard bits. In codebases written mostly by AI, better code ships faster. Once the hard machinery is sealed behind reliable contracts, the features on top stop being regression risks and become simple composition. It isn't "better, but narrower." It's better, and therefore cheaper to expand. Breadth is the dividend on quality.
Seam selection
The human doesn't disappear; the engineer and the model become peers with different strengths, and the model is far better at grinding out implementation in unfamiliar territory. What stays human is product taste, algorithmic design, deciding what should be falsifiable — and above all, seam selection. "Make it a pure function" is easy once you know where the function goes; the skill is finding the true hard edge of the system and cutting there. A narrow entrance in the wrong place is just a tidy mistake. And that's only half the job: the model's default pull is back toward coupling, so the other half is refusal — holding the boundary it would happily dissolve. Cutting is the judgment; holding is the discipline; the discipline is where the leverage lives.
Which is the whole thing in one paradox: the frontier subsystems take a day, and the buttons are the stubborn part. The button costs a full reconstruction of everything it touches, and coupling decides how much that is; the deep subsystem costs only its contract. AI-friendly architecture isn't an optimisation on top of how we already build. Under the new cost model it is what good architecture means: deep local code cheap, connection violently expensive, the ambient-attention subsidy gone. Match the prices and the impossible collapses to a day; refuse, and even the buttons fight you.
Write better software; the volume will follow.
Notes
The viewer outruns the SDK the industry runs on. Apryse is the PDF-viewer SDK a large share of construction tech is built on. Easy to test: load the same heavy CAD PDF in Apryse's browser demo [showcase.apryse.com/cad-viewer] and the Purple Hammer web preview [purple-hammer.com], same machine, and compare first render, time-to-sharp after zoom, and pan/zoom latency. Light files can hide the gap, so if both seem "instant," look in the performance tab. A desktop comparison against Bluebeam/PlanSwift is even cleaner if you have them — open a heavy set, measure time to ready/sharp. Same file, same hardware, same class of workload.
RAM and solve speed don't scale with sheet size. Generate a CSV of a few hundred thousand rows, import it into Purple Hammer (free trial), and watch responsiveness and RAM. Then write an aggregating formula over the large surface and watch compute time: memory scales with the query, not the workbook. Compare it to something like DuckDB — reads are slightly slower, but writes are orders of magnitude faster, and RAM grows far less with size. Different systems, different tradeoffs; the point is that this performs within an order of magnitude of cutting-edge systems many times larger.