Shape the Codebase Around Deep Modules with Simple Interfaces
Core Idea
The architecture of the codebase directly affects how well an agent can navigate, test, and modify it. Deep modules with small interfaces and substantial internal functionality are easier for agents and humans to reason about than a network of shallow modules with many scattered dependencies.
How It Works
A shallow-module codebase contains many small files that export small pieces of behavior and depend on one another in complex ways. To understand one change, the agent must traverse a broad dependency graph. Testing is also ambiguous:
- Should every tiny function have its own test boundary?
- Should neighboring files be mocked?
- Should a large group be tested together even though its public behavior is unclear?
Agents often respond by wrapping each small function in an isolated test. Those tests may prove that the pieces work separately while missing ordering errors, dependency interactions, or the behavior of the larger system.
A deep module exposes a small, stable interface while containing a large amount of implementation behind it. The caller interacts with a few clear operations. The module can be surrounded by a meaningful test boundary that covers substantial behavior.
This architecture supports a useful division of responsibility. The human designs the interface and understands the module's purpose, inputs, outputs, and behavior under important conditions. The agent can implement the internal details. The module becomes a gray box: its shape and guarantees are known, while every internal line does not have to remain in the human's working memory.
The module map should be considered during planning, not after the code is generated. A destination document can identify a new deep service, the interface it should expose, and the existing routes or services it will modify. The implementation issues then preserve that structure.
An architecture-improvement skill can scan the repository for clusters of tightly related modules, untested logic, and opportunities to deepen interfaces. It can explain why a set of functions is coupled and propose a larger unit that can be tested from the outside.
Why It Matters
Bad codebases produce bad agent outcomes because the agent has weak boundaries and weak feedback. A tangled dependency graph consumes context during exploration and makes it hard to predict the consequences of a change.
Deep modules improve both testability and human comprehension. Developers can move quickly without losing the high-level shape of the system. They need to remember the major modules and interfaces rather than every delegated implementation detail.
Practical Application
Use this refactoring method:
- Map the current modules and the dependencies between them.
- Identify clusters of files that together provide one meaningful capability.
- Define a small interface for that capability.
- Move the related logic behind the interface.
- Create tests around the external behavior of the deeper module.
- Update callers to depend on the interface rather than internal pieces.
- Record the module and interface explicitly in future product and implementation plans.
- Delegate internal implementation only after the interface and test boundary are clear.
For an end-to-end capability, the deep module may span a large flow. A browser-based editor, for example, can be structured so that an action at the interface is traceable through the back end and testable from the outside. Giving the agent a visible full flow and a reliable test boundary can transform its ability to make changes safely.
Trade-Offs and Limitations
Agents left unaided tend to produce more shallow modules, not fewer. They create files and abstractions locally without maintaining a coherent module map. Human architectural direction remains necessary.
A deep module is not permission to ignore its internals indefinitely. Its interface and tests must accurately represent the behavior that matters. If the boundary is wrong, the module can hide complexity rather than control it.
Refactoring toward deeper modules is itself risky work. It should be performed with feedback loops and in bounded steps rather than as an untested repository-wide rewrite.
Key Takeaway
Design the major interfaces, test behavior at meaningful boundaries, and delegate the internals. Deep modules reduce context cost, improve feedback, and let humans retain a usable mental model of an agent-modified codebase.