AI guide / Nate Hamilton
The 16 Architectural Boundaries for AI Systems
The 16 lines I draw around an AI system in production: where the model decides, where code must enforce, and why the boundary matters.
This is a condensed reference for engineers shipping AI into production: where each boundary belongs, what it owns, and what tends to fail without it.
Last updated
The through-line
A line the system enforces
An architectural boundary is a line the system enforces regardless of what the model does. It defines which code owns a responsibility, what can cross the interface, and which operations are available. The model can propose a decision; the surrounding code validates its shape, checks authority, and controls its effects.
Why 16? These are the 16 seams this guide follows, in roughly the order they bite when skipped. They separate things that change, fail, and need review differently, from the runtime and domain rules to the interfaces that expose the system. They belong together because each boundary gives the next one something stable to depend on.
Access and dispatch
Agent execution and context
Domain and decision contracts
State and external effects
Controls spanning all groups
Separate: build-time vs runtime
All 16 seams, grouped by responsibility. Evaluation, feedback, and cross-cutting controls span the groups. Boundary 14 separates reviewed code artifacts from live output; each needs its own checks. Original chapter numbers are preserved.
For the human operating model, read the guide. For buses, heartbeats, gates, and receipts, read AI Agent Operations.
01 / Architectural boundaries
The Agent Runtime Boundary
The runtime runs the agent; business code gives it work through a small, typed public interface. Keep model calls, tool dispatch, retries, conversation state, and token telemetry inside the runtime. Business modules receive a result or a typed error without importing the model SDK.
Why it matters. Without this line, every SDK upgrade reaches into business rules, and every feature invents its own retry policy. A domain test should not need a live model to check a state transition.
Back to index ↑02 / Architectural boundaries
The Model Abstraction Layer
Inside the runtime, translate provider APIs into one stable model interface. Keep provider-specific request shapes, response types, and errors inside their implementations. Declare capabilities explicitly and put task routing in configuration.
Why it matters. Otherwise, trying another model means changing feature code and its tests. A provider outage becomes an integration rewrite at exactly the wrong time.
Back to index ↑03 / Architectural boundaries
The Pure Domain Layer
Express business rules as pure functions over typed data, with no infrastructure dependencies. Pass in everything the rule needs, including the current time; the caller fetches data and performs side effects. Keep each rule in one authoritative place.
Why it matters. Rules scattered across controllers, storage, and UI drift apart. In a regulated decision workflow, I need to show the exact rule and test it without a database, network, or model.
Back to index ↑04 / Architectural boundaries
Persistence with Typed Accessors
Put every database read and write behind a typed repository function. The repository validates inputs, enforces tenant scope, and exposes a small set of deliberate operations; agents do not get a raw query interface. Keep the database client private to that layer.
Why it matters. A plausible query can still touch the wrong records or omit tenant scope. A constrained access surface bounds what the caller can do and keeps schema changes local to the repository.
Back to index ↑05 / Architectural boundaries
The Domain Plugin Architecture
Make each business vertical a self-contained module implementing a shared, versioned contract. The plugin supplies its fields, terminology, state machine, rules, and evaluation cases. Platform code calls the contract and never branches on the vertical's name.
Why it matters. Without this seam, each new vertical adds conditions across the same files. A change for one workflow then risks breaking every other workflow that shares the platform.
Back to index ↑06 / Architectural boundaries
The Context Assembly Pipeline
Give one pipeline ownership of everything that enters the model's context window. It selects sources, filters and orders content, formats the result, and enforces explicit token budgets. Return a typed context record with inclusion and exclusion decisions and enough information to replay what the model saw.
Why it matters. Missing evidence and excess noise can both produce a wrong answer. If context is assembled ad hoc and never captured, the inputs that caused a production failure may be impossible to reconstruct.
Back to index ↑07 / Architectural boundaries
The Evaluation Infrastructure
Treat evaluation as owned infrastructure with versioned cases, explicit scorers, baselines, and a CI gate. Run it against production model routing and make regressions beyond the noise floor block a merge unless an explicit exception is documented. Turn production failures into cases and keep the suites representative.
Why it matters. A model, prompt, or retrieval change can reduce quality while latency and error dashboards stay green. A passing build cannot tell you whether the AI still makes the right decisions.
Back to index ↑08 / Architectural boundaries
Decision Interface Contracts
Every AI decision has a typed input, typed output, and typed evidence trail. Validate the result as it leaves the model, and reject output that fails the schema. Store an immutable, versioned decision with an ID; downstream code reads fields, while free-text rationale is for presentation.
Why it matters. Parsing a recommendation's prose makes downstream behavior depend on phrasing. Structured evidence also lets a reviewer trace which inputs supported a decision instead of relying on the model's explanation alone.
Back to index ↑09 / Architectural boundaries
The Agent Communication Protocol
Agents exchange a small set of typed, versioned messages for requests, responses, handoffs, status, and errors. Link replies to requests and messages to a workflow ID. Persist workflow state in a typed store outside the conversation history.
Why it matters. A handoff in prose can lose a condition while preserving the apparent instruction. State held only in a conversation also disappears or gets compressed as a long workflow grows.
Back to index ↑10 / Architectural boundaries
Feedback Loop Infrastructure
Capture every production decision as a structured record, then link its eventual outcome and any human override back to that decision. Give capture, curation, and consumption an owned pipeline and a store with explicit retention. Use the curated records to seed evaluations and support model or retrieval improvements.
Why it matters. Production use does not teach the model by itself. Without the link from prediction to outcome, the team loses its correction signal and keeps measuring against an increasingly stale picture of the world.
Back to index ↑11 / Architectural boundaries
The External System Boundary
Put every external integration behind an adapter that accepts typed intent and returns a typed result. The adapter owns validation, secrets, retries, idempotency, and a durable audit trail, including the external reference. Agents never call the external service directly or receive its credentials.
Why it matters. A timeout does not establish that an action failed; retrying it can duplicate the action. The adapter provides a single place to contain that failure and reconcile what the external system actually did.
Back to index ↑12 / Architectural boundaries
The Async Orchestration Layer
Run AI work as queued jobs with IDs, deadlines, priorities, typed results, and queryable status. The request handler enqueues work and returns a handle; workers call the runtime under explicit concurrency and tenant budgets. Categorize failures, bound retries, and give dead-letter jobs an owner.
Why it matters. An inline model call can outlast the request that started it, leaving the user with an error while the system still pays for the work. Unbounded calls and retries turn a traffic spike or slow provider into a wider outage.
Back to index ↑13 / Architectural boundaries
The Tool Exposure Layer
Expose agent capabilities through a discoverable registry of tools with descriptions and typed input and output schemas. Enforce permissions and tenant scope inside invocation, and audit every call. The same definitions can serve internal agents and an MCP interface.
Why it matters. A capability hidden behind a client-specific endpoint gets wrapped again by every new agent. Permissions enforced only at one entry point leave other invocation paths without the same check.
Back to index ↑14 / Architectural boundaries
Build-Time vs Runtime Generation
Separate generation that produces code from generation that produces live behavior. Generated code belongs in reviewable, committed artifacts and passes the same lint, typecheck, and test gates as handwritten code. Runtime output needs evaluations, validated contracts, bounded capabilities, and feedback capture; give the two separate packages, budgets, and metrics.
Why it matters. Lumping both into 'AI generation' invites the wrong gate for the job. A code review process cannot stand in for controls on every live decision, and a generator's track record cannot excuse unreviewed code.
Back to index ↑15 / Architectural boundaries
Cross-Cutting Concerns
Give configuration, logging, errors, IDs, clocks, request context, and telemetry an explicit shared home. Expose one canonical interface per concern, with no dependency on feature code. Centralize redaction and configuration validation, and make clocks and other test dependencies injectable.
Why it matters. When each package rolls its own, logs stop joining up, configuration fails differently, and sensitive-data handling drifts. Agents copying existing code then reproduce that inconsistency in every new feature.
Back to index ↑16 / Architectural boundaries
Access Surface Convergence
Send API, MCP, chat, dashboard, and webhook commands through one processing pipeline. Surface adapters handle authentication, rate limits, parsing, and formatting; the shared operation owns authorization, business rules, state transitions, and side effects. Every surface resolves to the same actor, tenant, and permission model.
Why it matters. Separate implementations produce parity bugs: the dashboard action sends a notification while the API action forgets it. One operation must enforce the same rules and produce the same effects however it is reached.
Back to index ↑Keep going
Put the boundaries into practice
Read the other AI Guides for worked failures, delegation patterns, operating checks, and concrete steps you can apply to your own system.
Browse AI Guides