Boundary
Context
Every system has places where one part stops and another begins. A boundary is that dividing line: the membrane between inside and outside, between “my responsibility” and “yours.” Boundaries operate at the architectural scale and are among the most important structural decisions in any system. They determine what a component owns, what it exposes, and what it keeps hidden.
Boundaries exist at every level: between functions, between modules, between services, between organizations, and between a human and an AI agent. Wherever there is an interface, there is a boundary behind it.
Problem
Where should you draw the line between one part of a system and another, and how do you enforce it?
Forces
- Boundaries that are too coarse leave you with large, tangled units that are hard to change.
- Boundaries that are too fine create communication overhead and indirection.
- Some boundaries are natural (they align with the domain), others are arbitrary (they align with organizational charts or deployment constraints).
- Boundaries that aren’t enforced erode over time. Code reaches across them, and soon the boundary exists only on paper.
Solution
Place boundaries where the rate of change differs, where ownership differs, or where the domain naturally divides. A boundary should separate things that can evolve independently. The classic test: if you change something on one side of the boundary, how much changes on the other side? If the answer is “a lot,” the boundary is in the wrong place, or the coupling across it is too high.
Enforce boundaries with mechanisms appropriate to the context. In a single codebase, use module visibility rules and code review. Between services, use explicit APIs and contracts. Between teams, use documented agreements and integration tests.
In agentic coding, boundaries serve a practical purpose beyond software design: they scope the agent’s work. When you tell an agent “work within this module,” the boundary tells the agent what files to read, what interfaces to respect, and what not to touch. Clear boundaries make agent instructions precise. Fuzzy boundaries force the agent to guess, and agents guess wrong in expensive ways.
How It Plays Out
A backend team draws a boundary between their API layer and their data access layer. The API layer handles HTTP concerns (routing, serialization, authentication). The data access layer handles persistence (queries, caching, transactions). Neither layer reaches into the other’s internals. When the team later migrates from one database to another, the API layer does not change at all.
An AI agent is tasked with adding a feature to a large repository. The developer scopes the task: “Work only within the notifications/ directory. The interface with the rest of the system is the NotificationService class — do not change its public methods.” This boundary instruction lets the agent make confident changes without risking side effects elsewhere in the codebase.
“Work only within the notifications/ directory. The interface with the rest of the system is the NotificationService class — don’t change its public methods. You can refactor anything inside the module freely.”
Consequences
Well-placed boundaries make systems easier to understand, test, and evolve. They enable ownership: a team or agent can be responsible for everything inside a boundary. They contain failures, so a bug behind one boundary is less likely to cascade across the system.
The cost is the overhead of crossing them. Every boundary implies an interface, and every interface introduces indirection. If you draw too many boundaries, you spend more time marshaling data across interfaces than doing actual work. The right number is the minimum that lets each part evolve independently.
Related Patterns
- Hosts: Interface — an interface lives at a boundary.
- Enforces: Contract — contracts are verified at boundaries.
- Defines: Component, Module — boundaries define what is inside a component or module.
- Measured by: Coupling — the amount of traffic across a boundary indicates coupling.
- Produced by: Decomposition — decomposing a system creates new boundaries.
- Informed by: Domain Model — domain boundaries (bounded contexts) map to system boundaries.
- Informed by: Bounded Context — bounded contexts provide a domain-driven rationale for placing system boundaries.
- Violated by: Big Ball of Mud – mud has no meaningful boundaries.