Even when you move past raw "vibe coding" and enforce rigorous practices like test-driven development and dual-axis code reviews (as explored in our deep dive on Matt Pocock's Claude Code skills), an insidious problem often creeps into agent-maintained projects: architectural sprawl.
Your tests are green. Your linting is spotless. Yet every time you prompt your agent to make a change, you find it bouncing between twelve different single-purpose files, anemic utility classes, and paper-thin wrappers that do little more than hand off parameters.
What went wrong?
Without architectural guidance, AI agents naturally default to shallow modules. They create tiny classes and functions to satisfy local test requirements without considering holistic system design.
That is why Matt Pocock's improve-codebase-architecture skill is quietly the most powerful, underrated tool in the entire suite. It doesn't just check your code—it audits the structural geometry of your codebase, visualizes deepening opportunities in your browser, and guides you through eliminating architectural friction.
The Shallow Module Trap in the Age of AI
In A Philosophy of Software Design, John Ousterhout introduced a foundational distinction that has become essential for developers working alongside AI: Deep vs. Shallow modules.
- Shallow Module: Has a wide, complex interface relative to its implementation. It requires callers to learn many methods and juggle intricate parameters, but contains almost no real behavior—frequently acting as a glorified pass-through.
- Deep Module: Has a tiny, simple interface concealing rich internal complexity and robust invariants. It maximizes leverage for the caller and concentrates locality for the maintainer.
┌─────────────────────────────────┐ ┌─────────────────────┐
│ Large Interface │ │ Small Interface │ ← Few methods, simple params
├─────────────────────────────────┤ ├─────────────────────┤
│ Thin Implementation │ │ │
└─────────────────────────────────┘ │ Deep Implementation│ ← Complexity hidden & tested
(Shallow Module) │ │
High cognitive overhead, └─────────────────────┘
brittle caller coupling (Deep Module)
Maximum caller leverage
Why do AI agents generate shallow modules by default?
- Path of least resistance: When an agent is prompted to add a capability, creating a new helper file (
UserValidationHelper.ts,OrderStatusCoordinator.ts) carries zero risk of breaking existing tests in neighboring files. - Mock-driven rot: When developers insist on unit testing in isolation, agents slice logic into hyper-granular units so they can mock every side effect. The result is a sprawling web of interfaces where the real bugs hide in the glue code between them.
- Context window degradation: As shallow modules proliferate, future agent sessions become exponentially more expensive and error-prone. The agent must consume thousands of tokens jumping across fragmented files just to understand one cohesive business flow.
Deep vs. Shallow in Practice
Consider how an e-commerce checkout flow commonly evolves under unguided agent development:
The Shallow Architecture (High Friction, Low Locality)
// Callers must coordinate 4 distinct shallow services and know the precise order of execution
export class CheckoutController {
constructor(
private validator: CartValidator,
private taxCalculator: TaxCalculator,
private inventoryReservation: InventoryReservationService,
private paymentProcessor: PaymentGatewayAdapter,
private orderRepo: OrderRepository
) {}
async checkout(cartId: string, customerId: string, paymentToken: string) {
const cart = await this.orderRepo.getCart(cartId);
if (!this.validator.isValid(cart)) throw new Error("Invalid cart");
const tax = await this.taxCalculator.calculate(cart.items, customerId);
const reservation = await this.inventoryReservation.reserve(cart.items);
try {
const charge = await this.paymentProcessor.charge(cart.total + tax, paymentToken);
return await this.orderRepo.createOrder(cart, charge.id);
} catch (err) {
await this.inventoryReservation.release(reservation.id);
throw err;
}
}
}
Notice the architectural defects:
- The controller is acting as an ad-hoc workflow engine because the underlying domain modules are paper-thin.
- Testing
CheckoutControllerrequires mocking four separate collaborators. - If you apply Michael Feathers’ deletion test ("If I delete this module, does complexity vanish or reappear across callers?"), deleting any of these helpers simply moves the boilerplate straight back to the caller.
The Deep Architecture (High Leverage, Single Seam)
Refactoring this into a deep module radically simplifies the system:
export interface OrderPlacement {
placeOrder(input: {
cartId: string;
customerId: string;
paymentToken: string;
}): Promise<OrderResult>;
}
// All inventory holds, tax rules, payment handshakes, and rollbacks
// are hidden behind a single seam that callers—and tests—cross.
export class OrderService implements OrderPlacement {
constructor(
private db: DatabasePool,
private paymentGateway: PaymentGateway
) {}
async placeOrder(input: { cartId: string; customerId: string; paymentToken: string }): Promise<OrderResult> {
// Cohesive transactional implementation with automatic rollback & locality
}
}
In the deep version:
- The interface is the test surface: you test the behavior against an in-memory database and a payment gateway fake. No mock labyrinths.
- Callers have maximum leverage: they pass three parameters and get back a finished result.
- Maintainers have locality: when tax calculations or inventory hold rules change, the changes are contained in one place.
The 3-Phase Deepening Loop of /improve-codebase-architecture
The improve-codebase-architecture skill operationalizes this design philosophy into an automated, interactive workflow:
┌────────────────────────────────────────────────────────┐
│ 1. EXPLORE & SCOPE │
│ - Analyze Git hotspots (`git log --oneline`) │
│ - Read domain context (`CONTEXT.md` & `docs/adr/`) │
│ - Apply the Deletion Test to spot shallow modules │
└───────────────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 2. VISUAL HTML REPORT │
│ - Render self-contained report to OS temp directory │
│ - Interactive Tailwind layout + Mermaid diagrams │
│ - Before/After cards with recommendation badges │
└───────────────────────────┬────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────┐
│ 3. INTERACTIVE GRILLING & LIVING DOCS │
│ - Run `/grilling` on user-selected candidate │
│ - Update `CONTEXT.md` vocabulary inline │
│ - Record architectural trade-offs in `docs/adr/` │
└────────────────────────────────────────────────────────┘
Let's examine what makes each phase distinct.
Phase 1: Scoped Discovery and the Deletion Test
A common pitfall with automated architectural scanners is that they produce hundreds of theoretical, low-value refactoring suggestions.
Matt Pocock's skill avoids this by adhering strictly to YAGNI (You Aren't Gonna Need It):
- Hotspot Analysis: It inspects recent Git commit history (
git log --oneline) to identify files and directories that change frequently. Deepening a module only pays dividends if developers and agents will actively interact with it in the future. - Friction-Based Exploration: A sub-agent explores the codebase asking concrete structural questions:
- Where does understanding a single concept require bouncing between multiple tiny modules?
- Where are pure functions extracted solely for unit testability while real bugs hide in orchestrating them?
- Which modules leak their internal concerns across their seams?
- The Deletion Test: For every suspected shallow module, the agent asks: If we delete this, does complexity concentrate or merely scatter? Only candidates that genuinely concentrate complexity make the cut.
Phase 2: A Masterclass in Agent UX (Ephemeral HTML Reports)
Dumping massive architectural analyses into the CLI or the LLM's chat context creates two problems: it pollutes your terminal and exhausts context tokens.
The improve-codebase-architecture skill solves this with an elegant UX pattern: it writes a standalone, self-contained HTML report to your operating system's temp directory and launches it directly in your browser.
Styled with Tailwind CSS via CDN and animated with Mermaid.js diagrams, the report renders a visual dossier for every deepening opportunity:
- Files Involved: Clear mapping of affected files and modules.
- The Problem: Concrete explanation of the architectural friction.
- The Solution: Plain-English description of the proposed deepening.
- Before / After Diagrams: Side-by-side architectural comparisons contrasting shallow call graphs with deep encapsulations.
- Confidence Badges: Categorized as
Strong,Worth exploring, orSpeculative. - Top Recommendation: An explicit verdict on which seam to tackle first and why.
Because the file lives in your OS temp directory (/tmp or %TEMP%), your repository remains clean while you review high-density visual architecture in your browser.
Phase 3: Interactive Grilling and Living Documentation
Once you review the report and select a deepening candidate, the skill transitions to execution through an interactive Grilling Loop (using the /grilling skill).
Instead of making speculative changes, the agent interviews you about the new interface:
- What invariants must live behind the seam?
- What configuration is necessary?
- Which legacy tests will be superseded by testing through the newly unified interface?
Crucially, this phase updates your repository's living documentation in real time:
- Glossary Alignment: If a deepened module introduces or refines a domain concept, it updates
CONTEXT.mdimmediately. - Architecture Decision Records: If you reject a candidate or choose an unconventional seam for load-bearing reasons, the agent offers to record an ADR in
docs/adr/. Future agent sessions will read this record, preventing them from repeatedly proposing the same rejected refactor.
Why This Skill Is Essential for Autonomous Agents
Most conversations around AI-assisted coding focus on execution: writing code faster, fixing syntax errors, or running test suites.
But when agents generate code faster than ever before, architectural decay accelerates at the same rate.
If your codebase consists of shallow modules with leaky seams:
- Every agent prompt requires broader file searches.
- Token consumption climbs with every task.
- Subtle integration bugs multiply in untested glue code.
By periodically running:
/improve-codebase-architecture
you actively prune shallow abstractions, reinforce deep interfaces, and create an environment where both human developers and autonomous AI agents can operate with clarity, speed, and confidence.