Here is something that only becomes visible in retrospect.
Every pattern we used to make DarJS composable — contracts as plain objects, pure generators, escape hatches, fake adapters, phase specs as self-contained documents — was solving the same problem that multi-agent systems need solved. We weren’t designing for agents. We were designing for junior developers, for testability, for clean boundaries. The agent-readiness came for free.
This is not a coincidence. It’s the same problem.
The problem is: how do you let multiple actors work on the same system without stepping on each other, without each actor needing to understand everything, and without the system becoming fragile when an actor is swapped out?
For software: the actors are developers, junior and senior, present and future.
For agents: the actors are models, fast and slow, specialized and general.
The solution is identical in both cases. Contracts.
What a Contract Does
A contract is a boundary with a precise description on each side. It says: here is what I expose, here is what I expect, here is what happens at the edge between us. Everything on your side of the boundary is your business. Everything on my side is mine.
A contract makes a system composable because it makes each piece independently understandable. You don’t need to know how PageDefGenerator works to use a PageDef — you need to know what a PageDef looks like. You don’t need to know how the RBAC engine evaluates wildcards to use the actions array in a template — you need to know it’s already filtered.
This is what makes a junior developer productive without deep framework knowledge. It’s also what makes an AI agent productive without deep codebase knowledge. The mechanism is identical.
The Patterns, Mapped
Every architectural pattern in this series has a direct parallel in multi-agent system design.
Contracts as plain objects.
In DarJS, PageDef is a plain JS object — not a class, not an instance. It can be serialized, inspected, round-tripped through module.exports, and passed between layers without instantiation.
In a multi-agent system, the handoff between agents is the same thing. A task description that’s a plain structured object — not a conversation, not a chain of reasoning — can be passed to any model, inspected by a supervisor agent, stored and retrieved, and verified against a schema. The agent that receives it doesn’t need to know how it was produced. The agent that produces it doesn’t need to know who will consume it.
Plain objects are the universal handoff format. Classes are not.
Pure generators. In DarJS, generators return strings and objects. They never write files. The CLI layer does the writing. This separation means the generation logic is testable without I/O, and the I/O layer is so thin it barely needs testing.
In a multi-agent system, the equivalent is an agent with bounded scope. An agent that only reasons and returns a structured output — never takes external actions directly — is testable, auditable, and safe to run in parallel. An agent that reasons and writes files and calls APIs is a monolith. It’s hard to test, hard to supervise, and when it fails you don’t know which part failed.
Pure function agents — agents whose output is a return value, not a side effect — compose the same way pure functions do.
Escape hatches.
In DarJS, the 3-tier lookup gives the developer a way to override any layer without modifying the framework. Drop a .njk file, the generic renderer steps aside. The escape hatch is documented in the spec before it’s needed.
In a multi-agent system, this is the human override point. The architecture layer produces a spec. The implementation layer follows it. But the human can intercept at any layer — review the spec before implementation, reject an output before it’s committed, redirect a task mid-flight. The system needs explicit points where human judgment can enter without requiring a full restart.
A system with no escape hatches isn’t a system. It’s a pipeline. Pipelines break in interesting ways and are hard to correct.
Fake adapters.
In DarJS, MemoryAdapter implements the full database interface in memory. Tests run against it. The production PrismaAdapter implements the same interface. If tests pass against the fake, they pass against the real — because both honor the contract.
In a multi-agent system, this is the sandbox. You test a new agent against a fake environment before letting it touch real systems. The fake environment implements the same interface as the real one — same tool signatures, same response shapes. An agent that behaves correctly in the sandbox will behave correctly in production, because the contract is the same.
The fake isn’t a mock. It’s a parallel implementation. The distinction matters: a mock proves the agent calls the right tools. A fake proves the agent produces the right outcomes.
Phase specs as self-contained documents. In DarJS, every phase spec was written to be readable in isolation. A model entering the project mid-build could read phase 8’s spec and implement it correctly without reading phases 1–7. The spec contained everything needed: what to build, what the interface must satisfy, what the test count meant done.
In a multi-agent system, this is the task prompt. A task prompt that requires the receiving agent to read prior context to understand it is an incomplete task prompt. Each task must be self-contained — all necessary context embedded, nothing assumed from memory or history.
This is harder to write than it sounds. The temptation is always to abbreviate, to reference prior work, to say “as discussed.” Self-contained task prompts require the architecture layer to be explicit about everything the implementation layer needs. That explicitness is the work. It’s also what makes the system reliable.
Decisions files.
In DarJS, each phase has a companion decisions/phase*.md that records what was rejected and why. The road not taken.
In a multi-agent system, this is the context that prevents re-evaluation. When a new agent joins the project — a new session, a different model, a specialized tool — it doesn’t re-examine settled questions. The decisions file is the artifact that makes accumulated judgment portable. Without it, every new agent reasons from scratch and may reach a different conclusion. With it, the project’s history is legible to any actor who needs it.
The Deeper Point
Software engineering developed contracts, pure functions, separation of concerns, and layered abstraction because humans have limited working memory, limited context windows, and limited ability to reason about systems too large to hold in mind at once.
AI agents have the same constraints. A context window is a context window whether it belongs to a human or a model. The patterns that help a human developer work on a large system without understanding all of it help an AI agent for exactly the same reasons.
This means good software architecture and good agent architecture converge. They were always solving the same problem. The rise of agents just makes the solution more urgent and more visible.
A system built with clean contracts, pure functions, and explicit handoffs is ready for agents not because it was designed for agents, but because it was designed well. The agent-readiness is a consequence of the quality.
What This Means in Practice
If you’re building a system today that will involve AI agents — and most serious systems will, within a few years — you don’t need a new architecture. You need the architecture you should have been building anyway.
Identify your layers. Make the boundaries between them explicit. Describe the contracts precisely. Keep the generators pure. Document the escape hatches. Write self-contained specs. Record what was rejected and why.
Each of these is independently valuable for human developers. Together, they produce a system that any agent — or any developer, junior or senior, present or future — can work on productively without understanding the whole.
That’s the goal. It always was. Agents just make it visible how much it matters.
“Good software boundaries and good agent boundaries are the same thing.”