Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Boundary

Concept

A foundational idea to recognize and understand.

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.

Example Prompt

“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.

Sources

  • David Parnas’s “On the Criteria To Be Used in Decomposing Systems into Modules” (Communications of the ACM, 1972) is the foundational argument for placing boundaries around design decisions that are likely to change. The “rate of change differs” criterion in the Solution section is Parnas’s information-hiding principle restated for the agentic era.
  • Eric Evans’s Domain-Driven Design (Addison-Wesley, 2003) supplies the domain-driven rationale for where major boundaries fall, through the bounded-context concept referenced in the front matter. Evans argues that model boundaries should follow regions where a particular language and meaning apply, rather than organizational charts or deployment topology.
  • Michael Nygard’s Release It! (Pragmatic Bookshelf, 2nd ed. 2018) frames boundaries as failure-containment devices in distributed systems. The bulkhead pattern — boundaries sized so that a fault inside one cannot sink the whole ship — is the practical form of the “contain failures” consequence in this article.