Deep Modules vs Shallow Modules in Software Architecture: Design Principles Explained

Deep modules expose minimal, simple interfaces that encapsulate substantial implementation complexity, whereas shallow modules present large, verbose interfaces that offer little abstraction and force callers to manage intricate details.

The mattpocock/skills repository provides architectural guidance on distinguishing deep modules vs shallow modules as documented in tdd/deep-modules.md. These principles are supported by additional references in improve-codebase-architecture/REFERENCE.md and contextualized within test-driven development practices in tdd/SKILL.md.

What Defines Deep Modules?

Deep modules feature small, focused interfaces that hide substantial implementation detail behind a few well-named methods with simple parameters. The interface contract remains stable while the internal logic—often complex SQL building, transaction handling, or business rules—evolves independently without breaking callers.

Characteristics of Deep Module Design

  • Minimal surface area: Few public methods that perform significant work.
  • Simple signatures: Parameters are intuitive and self-contained, requiring no knowledge of internal state.
  • Rich implementation: Complex logic lives inside the module, insulated from external code in tdd/deep-modules.md.

Understanding Shallow Modules

Shallow modules offer large, verbose interfaces that expose many methods and complex parameters while providing minimal internal logic. These modules typically act as thin wrappers that forward calls to other components, forcing developers to understand and manage numerous implementation concerns.

Problems with Shallow Interfaces

  • Implementation leakage: Callers must understand internal dependencies and configuration flags.
  • Refactoring risk: Changes ripple through the verbose interface to all clients.
  • Cognitive overload: Developers juggle sprawling method signatures and excessive parameters that belong inside the module.

Design Questions for Deep Module Architecture

The tdd/deep-modules.md file outlines three critical questions to evaluate whether your modules achieve proper depth:

  1. Can the number of methods be reduced? Fewer entry points mean less coupling and clearer abstraction boundaries.
  2. Are the method signatures simple enough? Parameters should not require callers to assemble complex object graphs or understand internal state machines.
  3. Is more complexity being hidden inside the module rather than exposed? The module should absorb complexity, not redistribute it to clients.

Deep vs Shallow Modules: Code Examples

Deep Module Implementation (Go)

The repository presents a Go example demonstrating how a repository pattern can encapsulate database complexity behind a minimal interface:

// Repository defines a minimal contract for persisting users.
type Repository interface {
    Save(user *User) error
}

// sqlRepository implements the Repository interface.
// Internally it contains complex SQL building, transaction handling, etc.
type sqlRepository struct {
    db *sql.DB
}

func (r *sqlRepository) Save(user *User) error {
    // Complex logic hidden from callers.
    tx, err := r.db.Begin()
    if err != nil { return err }
    // … many lines of SQL preparation and execution …
    return tx.Commit()
}

The Save method signature remains trivial—accepting only a *User—while the implementation handles transaction management, error handling, and SQL generation internally.

Shallow Module Implementation (Java)

Contrast this with the shallow module example, where the interface forces callers to understand domain-specific flags and configuration:

public interface UserService {
    void createUser(String name, String email, String password, boolean admin,
                    List<String> roles, Map<String, Object> metadata);
    void deleteUserById(UUID userId, boolean softDelete, boolean cascade);
    // … many more methods with sprawling signatures …
}

This design leaks implementation concerns (soft delete flags, cascade options) and requires callers to assemble complex parameter lists for every operation.

Summary

  • Deep modules prioritize small interfaces that hide complex implementations, making refactoring safer and client code cleaner according to tdd/deep-modules.md.
  • Shallow modules expose verbose interfaces with minimal logic, increasing coupling and cognitive load across the codebase.
  • The mattpocock/skills repository recommends evaluating modules through three lenses: method count reduction, signature simplicity, and internal complexity encapsulation.
  • Code examples demonstrate how a single Save method can abstract entire transaction workflows, while sprawling parameter lists force clients into implementation details.

Frequently Asked Questions

What makes a module "deep" in software architecture?

A deep module provides a narrow interface—often just one or two methods—that encapsulates substantial implementation complexity. According to the mattpocock/skills repository, deep modules hide details like database transactions, validation logic, and error handling behind simple signatures, allowing the implementation to evolve without breaking client code.

Why are shallow modules considered harmful to codebases?

Shallow modules expose large interfaces with many parameters and methods but contain little actual logic, essentially acting as pass-through layers. This forces callers to understand internal implementation details and coordinate between multiple parameters, making the code harder to refactor and increasing the surface area for bugs.

How can I refactor shallow modules into deep modules?

Start by examining method signatures in interfaces like those shown in tdd/deep-modules.md. Consolidate related operations into cohesive methods with fewer parameters, move complex orchestration logic inside the module, and eliminate boolean flags or metadata maps that leak implementation strategies. The goal is reducing the interface width while increasing the implementation depth.

Where can I find more resources on deep module design?

The mattpocock/skills repository contains architectural guidance in tdd/deep-modules.md and supplementary references in improve-codebase-architecture/REFERENCE.md. These documents provide additional patterns for identifying shallow modules and strategies for creating deep abstractions in test-driven development contexts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →