Improve Codebase Architecture
Systematic architecture improvement: build the vocabulary, map the terrain, design competing interfaces, then test at seams.
Full Glossary
These eight terms are the shared vocabulary for every architecture improvement conversation. Internalize them before beginning any analysis.
Core Architecture Vocabulary
Module
A cohesive unit of code that encapsulates a single responsibility. A module has a public interface and hides its implementation. The boundary of a module is defined by what it exports, not by what directory it lives in.
Avoid: package, folder, file, component (when used loosely)
Interface
The contract through which a module is used. An interface defines inputs, outputs, and behavior guarantees without specifying implementation. In dynamically typed languages, the interface may be implicit (a duck-typed protocol) rather than explicit (a TypeScript interface or Go interface).
Avoid: API (overloaded), class (too narrow), type (too narrow)
Implementation
The code inside a module that fulfills the interface contract. Implementations are interchangeable as long as they satisfy the same interface. The goal of architecture improvement is often to make implementations truly interchangeable.
Avoid: internals, guts, logic, code
Depth
The ratio of interface complexity to implementation complexity. A deep module has a simple interface that hides a complex implementation — high leverage. A shallow module has an interface nearly as complex as its implementation — low leverage, often a sign of poor abstraction.
Avoid: abstraction level (vague), layer (implies hierarchy)
Seam
A place in the code where behavior can be changed without modifying the code on either side of the seam. Seams are testing surfaces. Every dependency injection point, interface, and function parameter is a potential seam.
Avoid: injection point (too narrow), hook (overloaded), extension point (implies plugin architecture)
Adapter
A module that translates between two interfaces. An adapter wraps a specific implementation behind a general interface. Adapters enable implementation substitution at seams. A single adapter is a design hypothesis; two adapters with one interface is evidence of a real abstraction.
Avoid: wrapper (less precise), proxy (implies network), facade (implies simplification only)
Leverage
The ratio of behavior change enabled to code changed. High-leverage code changes (modifying an interface) affect all users of that interface. Low-leverage code changes (adding a null check inside one function) affect only that function.
Avoid: impact (vague), scope (vague)
Locality
The degree to which code that changes together lives together. High locality means you can make a coherent change to one area of the codebase without touching many unrelated files. Low locality is the root cause of 'shotgun surgery' — a single logical change requiring edits in many places.
Avoid: cohesion (academic), coupling (the inverse concept, not the same thing)
Key Principles
Process: Explore → HTML Report → Grilling Loop → Interface Design
Dependency Categories
Every dependency in a codebase falls into one of four categories. Each category has a different testing and improvement strategy.
| Category | Definition | Test strategy | Improvement strategy |
|---|---|---|---|
| In-process | Another module in the same process. Imported directly. | Unit test at the interface. No mocking needed if the module is fast and deterministic. | Apply the deletion test. Introduce a seam if two implementations exist. |
| Local-substitutable | An external dependency that can be replaced with a local version for tests (in-memory database, file system, etc.). | Use the local substitute in tests. The real dependency in integration tests. | Ensure a clear interface exists. Do not let callers use the real and substitute implementations interchangeably in production. |
| Remote-but-owned | Another service we own and operate. We control both ends of the connection. | Contract tests: both producer and consumer own a shared contract test suite. | Define a client interface. One adapter per client instance. Test with the contract. |
| True external | A third-party service we do not control (Stripe, SendGrid, AWS, etc.). | Mock at the adapter boundary only. Never mock the true external service inside business logic. | Create an adapter that wraps the external SDK. The business logic depends on the adapter interface, not the SDK. |
Seam Discipline and Testing Strategy
A seam is a point where you can change behavior without modifying code on either side. Every seam is a test surface.