name: improve-codebase-architecture description: Find deepening opportunities in a codebase — refactors that turn shallow modules into deep ones for better testability and AI-navigability. USE WHEN user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, reduce friction, or make a codebase easier for agents to work in. Best run periodically (weekly or after a surge of development).
Improve Codebase Architecture
Surface architectural friction and propose deepening opportunities — refactors that turn shallow modules into deep ones. The goal is a codebase that is easy to test and easy for an agent to navigate, because a badly structured codebase makes agents produce badly structured code.
Grounded in John Ousterhout’s A Philosophy of Software Design: the best modules are deep — a lot of behaviour behind a small, simple interface.
Glossary
Use these terms exactly in every suggestion. Consistent language is the point.
- Module — anything with an interface and an implementation (function, class, package, file, slice).
- Interface — everything a caller must know to use the module: types, invariants, error modes, ordering, side effects, config. Not just the type signature.
- Implementation — the code inside.
- Depth — leverage at the interface: a lot of behaviour behind a small interface. Deep = high leverage. Shallow = interface nearly as complex as the implementation.
- Seam — where an interface lives; a place behaviour can be altered without editing in place.
- Leverage — what callers gain from depth: they get a lot while having to know little.
- Locality — what maintainers gain from depth: change, bugs, and knowledge concentrated in one place instead of scattered.
Key tests
- Deletion test — imagine deleting the module. If complexity vanishes, it was a pass-through (shallow, delete it). If complexity reappears, duplicated across N callers, it was earning its keep (deepen it).
- The interface is the test surface. If a module is hard to test through its interface, the interface is wrong — don’t reach for mocks, fix the seam.
- Shallow smell — many tiny files you must bounce between to understand one concept; pure functions extracted only for testability while the real bugs live in how they’re wired together.
Process
1. Explore
Walk the codebase organically — don’t apply rigid heuristics. Note where you experience friction as you read:
- Where does understanding one concept require bouncing between many small modules?
- Where are modules shallow — interface nearly as complex as the implementation?
- Where have pure functions been extracted just for testability, while the real bugs hide in how they’re called (no locality)?
- Where do tightly-coupled modules leak across their seams?
- Which parts are untested, or hard to test through their current interface?
Apply the deletion test to anything you suspect is shallow: would deleting it concentrate complexity, or just move it around? “Concentrates” is the signal.
For larger codebases, dispatch parallel explorer subagents (see the
dispatching-parallel-agents skill) over different areas and collect their friction notes.
2. Present candidates (markdown report)
Write a markdown report (the sensible default for a scannable review). An HTML report with diagrams is also fine if the user wants something more visual. Do NOT propose final interfaces yet. For each candidate:
- Files — which modules are involved
- Problem — why the current architecture causes friction (name the shallowness)
- Solution — plain-English description of the deepening, no code yet
- Benefits — in terms of locality and leverage, and how testing improves
- Before / After — a short sketch (ASCII or mermaid) of the shallow structure vs. the deepened one
- Strength —
Strong,Worth exploring, orSpeculative
End with a Top recommendation: which candidate to tackle first and why.
Then stop and ask: “Which of these would you like to explore?”
3. Deepening loop
Once the user picks a candidate, hand off to the grill-me skill to walk the
design tree for that refactor — constraints, dependencies, the shape of the
deepened module, what sits behind the seam, which tests survive. Only after
that shared understanding is reached do you propose the concrete interface and
implementation plan.
If the user rejects a candidate for a load-bearing reason that a future review would otherwise re-suggest, note it (e.g. a short ADR or a line in the project’s architecture notes) so the next run doesn’t re-litigate it.
Cadence
This is maintenance, not a one-off. Run it weekly or after a burst of feature work. Each pass that deepens a shallow module makes every later agent session produce better output — the gains compound.