AI Agents Don't Respect Service Boundaries Unless the Spec Says So
Adding tests after an AI agent writes code is rarely the right safety gate. Declaring boundary intent before the agent starts is.
That inversion matters because the failure mode teams are hitting right now is not about test coverage. It is about AI agents producing code that passes every test, satisfies every functional requirement in the spec, and still violates the service boundary contracts that three teams spent a quarter negotiating. The tests go green. The diff lands in review. Then a senior engineer notices that the new OrderService module is importing directly from the InventoryService database layer, bypassing the gRPC contract entirely, and the entire ownership model quietly falls apart.
This is not an agent bug. This is a spec gap.
Why Agents Have Zero Organizational Context
An AI agent optimizing against a spec is doing exactly what it should do: satisfying the stated constraints as efficiently as possible. If the spec says "implement order fulfillment with sub-200ms p99 latency," the agent will find the fastest path. If that fastest path involves reaching directly into an adjacent service's persistence layer rather than going through the defined API surface, the agent has no reason to avoid it. Nothing in the spec said it was prohibited. Nothing in the spec encoded which team owns which data. The agent does not attend your architecture review. It does not read the Confluence page titled "Service Ownership Principles v2." It has no model of organizational consequence.
Engineers who have been at a company for two years carry thousands of implicit constraints in their heads. They know the payments team guards its schema like a vault. They know that calling the user-profile service's internal _hydrate endpoint directly is technically possible and completely forbidden. They know that the infra team's Kafka topics are not a public API even though they are technically accessible. None of that knowledge is in any spec. It lives in people.
AI agents do not have people-knowledge. They have spec-knowledge.
When you leave service boundaries undeclared in a spec, you are not making a neutral choice. You are choosing to let the agent infer that boundaries either do not exist or are not a constraint it needs to honor. It will infer the latter, every time.
The Violation Pattern Is Consistent
Across teams using agent-assisted development at any serious scale, the cross-service violation pattern shows up in roughly the same shape. The agent is given a feature spec scoped to Service A. Service B exposes some internal state that would make the implementation faster or simpler. The spec does not mention Service B at all, because the engineer writing the spec was thinking about Service A. The agent touches Service B anyway, because the spec did not tell it not to, and touching Service B is the most direct path to satisfying the stated intent.
The result is code that is locally optimal and globally destructive. The feature works. The latency target is hit. But you have now created a hidden coupling that will surface as a production incident eighteen months from now when Service B is migrated to a new persistence layer and the team doing the migration does not know that Service A was taking a direct dependency on internals they thought were encapsulated.
This is not a theoretical concern. Any team that has done even a few months of serious AI-assisted development has a version of this story. The only variance is how far into the process the violation was caught.
Boundary Intent Is Not Architecture Documentation
Here is the position that senior engineers resist: service boundary constraints are not architecture documentation. They are not README content. They are not ADRs. They are spec-level constraints that must be declared explicitly and verified deterministically against every diff that touches a service boundary.
The resistance usually sounds like: "But our ownership model is in the docs. Engineers know to check it." That argument was defensible when all code was written by humans who had read the docs. It is not defensible when a significant portion of your diffs are generated by agents that have no access to docs outside the spec they were given.
You would not write a security spec that says "don't do anything insecure" and expect an agent to produce secure code. You enumerate the constraints: no raw SQL interpolation, no secrets in environment variables, no unauthenticated endpoints on the admin surface. The same discipline has to apply to service boundaries. "Don't cross service lines" is not a constraint. "This service must only consume the inventory.v2 gRPC contract and must not import from any inventory-service internal package path" is a constraint.
The difference between those two formulations is the difference between a boundary an agent might respect and a boundary an agent will respect.
ThunderLang's approach is to treat boundary declarations as first-class spec concerns, expressed with the same precision as functional requirements, and verified against every generated diff before the agent hands work back. The key word is "before." Catching a cross-service violation in code review means the violation was already generated, potentially already tested against, and definitely already in the agent's output artifact. Catching it in spec verification means it never enters the diff.
Making Boundary Constraints Machine-Verifiable
The practical objection here is usually about maintenance cost: if you have to declare every boundary constraint explicitly in every spec, the spec maintenance overhead becomes unsustainable. This is a real concern and it deserves a direct answer.
The solution is not to declare boundaries ad hoc in individual specs. It is to maintain a machine-readable service ownership manifest that every spec inherits constraints from automatically. Think of it as a boundary policy layer that sits above individual feature specs. The manifest declares which services own which domains, which API surfaces are public contracts, which internal namespaces are encapsulated, and which cross-service call patterns require explicit approval tokens in the spec.
When an agent is handed a feature spec, that spec includes or imports the boundary policy for the services in scope. The agent sees the boundary constraints as first-class constraints, not as ambient cultural knowledge. Verification against the generated diff checks both functional intent and boundary compliance in the same pass.
This architecture has a real cost: someone has to maintain the manifest, and it has to stay synchronized with actual service ownership as the organization evolves. That is a legitimate engineering investment. It is also a much smaller investment than the cost of discovering that your payments service has been quietly importing from your fraud detection service's internal scoring module for six months because an agent found it efficient and nobody caught it until a compliance audit did.
For teams starting this work, the ThunderLang getting-started documentation covers how to structure boundary constraints as verifiable spec concerns rather than advisory documentation, which is the specific pattern this article is advocating.
The Strongest Counterargument
The strongest objection to this position is that declaring boundary constraints in machine-verifiable specs is solving an organizational problem with a technical tool, and organizations should just write better specs before handing work to agents.
I agree with the second half of that. Writing better specs is exactly the argument. The disagreement is over what "better" means.
If "better" means clearer prose descriptions of what the feature should do, that does not solve the boundary problem. The agent can read clear prose and still have no basis for knowing that the elegant shortcut it found violates a team autonomy contract. If "better" means machine-verifiable constraints that include boundary ownership as a first-class concern, then we are saying the same thing in different vocabulary.
The reason I insist on the machine-verifiable framing is simple: prose intent is ambiguous by nature, and ambiguity is exactly what agents exploit when they optimize. A constraint that can be interpreted flexibly will be interpreted in whatever direction satisfies the optimization target. A constraint expressed as a verifiable assertion about import paths, API surface usage, or data domain access has no flex in it. The diff either satisfies it or it does not. That determinism is what makes the constraint enforceable at agent runtime rather than at human review time.
Organizational discipline and technical enforcement are not substitutes for each other. You want both. But if you have to pick the one that catches violations before the code exists versus the one that catches them after, machine-verifiable boundary constraints in the spec win every time.
Gate your first AI change
ThunderLang lets you declare what a change must satisfy, including service boundary ownership constraints, then gates AI-written code against that intent with a verify-diff and durable proof artifacts before the output ever reaches a human reviewer. Try it here.
The boundary between two services is not an architecture convention until it is a machine-checked assertion; until then, it is just a suggestion an agent was never told to follow.