
Fault Containment in the Age of AI-Generated Code: Why Vertical Slices Matter More Than Ever
In a post about his current development workflow, Robert C. Martin—better known as Uncle Bob and the author of Clean Code—made a provocative statement:
“My current strategy is to not read any of the code written by my agents.”
His reasoning is practical. Reading every generated line would eliminate much of the productivity gained from coding agents.
Instead, he surrounds them with constraints: unit tests, Gherkin tests, QA procedures, quality metrics, mutation testing, coverage, and other automated controls. Confidence comes from making generated code pass through a verification gauntlet.
This direction is increasingly understandable. If an agent can implement a feature in minutes but a developer needs hours to inspect every line, human review becomes the new bottleneck.
But when we stop relying on human eyes to understand every implementation, another requirement becomes much more important: fault containment.
Fault containment means structuring a system so that a defect remains inside a small, recognizable boundary. When something fails, engineers should be able to identify the responsible capability without tracing behavior across the entire codebase.
Automated tests can tell us that the system violated an expectation. They cannot guarantee that the responsible code is easy to locate, understand, or change. Generated code may pass every quality gate while still scattering one capability across endpoints, services, repositories, and shared utilities. Passing tests does not make tangled code easy to debug.
Vertical slices create code-level fault-containment boundaries
At the code and repository level, vertical-slice architecture is one of the most practical ways to create fault containment.
A traditional structure groups files by technical role:
controllers/ services/ repositories/ models/
It looks organized, but the implementation of one capability may be distributed across the entire repository.
A vertical-slice structure groups code by recognizable workflow:
features/
orders/
place-order/
cancel-order/
payments/
capture-payment/
refund-payment/
This relationship is important: the vertical slice defines the territory in which a fault should live.
If payment capture fails, its slice gives the investigation an obvious starting point. If the slice exposes only a small public interface, the fault has fewer paths into unrelated capabilities. If its internal modules are cohesive, the number of possible causes becomes smaller again.
Vertical slices therefore do more than make a repository look organized. They reduce the search area during an incident, limit accidental dependencies, give coding agents a bounded working context, and make generated behavior easier to replace without disturbing the rest of the system.
They do not replace runtime isolation mechanisms such as timeouts, bulkheads, circuit breakers, or separate processes. They provide code-level, dependency-level, and change-level containment.
Agentic coding increases the value of structural boundaries
As code generation accelerates, quality control begins to shift:
- From inspecting every implementation detail to verifying observable behavior
- From manually spotting defects to automatically enforcing constraints
- From trusting individual lines to testing the system through its contracts
- From asking how the code was written to asking whether it satisfies its requirements
This does not mean human review should disappear, particularly for security-sensitive or critical code. It means line-by-line review may no longer be the primary control.
When code is verified mainly through external constraints, architecture must supply strong internal boundaries. Tests establish confidence that the code behaves correctly. Vertical slices establish confidence that, when it does not, the failure can be contained and located. Each slice becomes a natural boundary for generation, testing, ownership, change, and failure investigation.
A feature folder is not enough
Moving files beneath a features directory does not create a boundary by itself. A real slice needs a recognizable owner, a small entry point, cohesive internal responsibilities, controlled dependencies, and tests that mirror production ownership.
The companion implementation demonstrates these ideas with one intentionally small CapturePayment workflow:
- CapturePaymentEndpoint translates the external request and response. It does not implement the operation.
- CapturePaymentHandler coordinates the complete workflow.
- CapturePaymentValidator and PaymentReviewPolicy keep boundary validation and business decisions explicit and pure.
- PaymentWrite is the narrow persistence contract owned by the workflow.
- InMemoryPaymentWriter implements that contract outside the feature.
- Focused tests mirror the workflow’s production location.
The dependency direction is deliberate:
application composition
-> feature entry point
-> feature behavior and feature-owned contracts
<- infrastructure adapters
Infrastructure may implement a feature-owned contract. Feature behavior must not import an infrastructure implementation. That distinction is easy to miss, but it is central to containment. The workflow describes the state change it needs without inheriting the database’s vocabulary or lifecycle. Its behavior can be tested without infrastructure, and its adapter can be replaced without rewriting the operation.
The implementation is deliberately small because the code is the technical explanation. The article does not need to reproduce every class or test when readers can follow the complete slice directly in the companion repository.
These constraints must be part of code generation
A coding agent should not receive only a functional instruction such as “implement payment capture.” It also needs persistent structural constraints:
- Identify the business capability and its owning slice before changing code.
- Keep entry points thin and complete operations in purpose-named handlers.
- Keep validators and policies focused and free of infrastructure access.
- Let features own the narrow contracts required by their workflows.
- Never import another feature’s internals.
- Do not create shared abstractions until ownership is genuinely shared.
- Mirror production ownership in the test structure.
These rules should not live only in a prompt that disappears after one conversation. The companion repository makes them versioned and discoverable through specific Markdown files:
- AGENTS.md defines how an agent must inspect, implement, validate, and report changes.
- specs/constitution.md records stable repository-wide constraints.
- specs/architecture/ defines feature ownership, purposeful modules, dependency direction, and persistence boundaries.
- specs/features/G001-capture-payment/ separates the feature plan, behavioral requirements, and validation evidence.
- docs/module-responsibility-audit.md records why each important module exists and who owns it.
- scripts/check-repository.ps1 turns structural expectations into an executable handoff gate.
The exact filenames matter less than their responsibilities. Stable architectural rules, current repository structure, feature requirements, and validation evidence should be explicit, versioned, and close to the code they govern.
If generated-code volume makes continuous manual review impractical, manually correcting structural drift afterward will be impractical too. Architecture must be enforced while code is being generated.
The question that matters
The central question is no longer only:
Does this generated code pass its tests?
We must also ask:
When it fails, will we know where to look?
Agentic development makes producing code cheaper. It does not make tangled systems cheaper to understand. Without enforced ownership, vertical slices are merely folders.
With explicit contracts, one-way dependencies, cohesive modules, mirrored tests, traceable specifications, and automated boundary checks, they provide something increasingly valuable in an AI-generated codebase:
A reliable place to look when the code is wrong.
Leave a Reply
Your e-mail address will not be published. Required fields are marked *