Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module

Pattern

A reusable solution you can apply to your work.

Context

Within a component, or within a system small enough not to need explicit component boundaries, code still needs to be organized. A module is a unit of code or behavior grouped around a single coherent responsibility. It operates at the architectural scale, bridging the gap between the large-scale structure of a system and the individual functions and classes that do the work.

In most languages, a module corresponds to a file, a package, a namespace, or a class. The specific mechanism varies, but the intent is the same: gather related things together and give them a shared identity.

Problem

How do you organize code so that related things are easy to find and unrelated things do not interfere with each other?

Forces

  • Code that changes for the same reason should live together.
  • Code that changes for different reasons should live apart.
  • Too many small modules create a navigation burden. You spend more time finding things than reading them.
  • Too few large modules create a comprehension burden. Each module does too much to hold in your head.

Solution

Group code by responsibility. A module should have one clear reason to exist, and everything inside it should relate to that reason. This is the principle of cohesion: the contents of a module belong together.

A good module has a name that tells you what it does (not how it does it), an interface that exposes what outsiders need, and an interior that hides the rest. The boundary between “public” and “private” is one of the most useful tools in a programmer’s kit. It lets you change the inside without breaking the outside.

When working with AI agents, well-defined modules are essential. An agent instructed to “modify the validation module” can open the relevant files, understand the scope, and make targeted changes. If “validation” logic is scattered across twenty files in three directories, the agent either misses pieces or has to load far more context than necessary.

How It Plays Out

A Python project organizes its code into modules: auth.py handles authentication, models.py defines data structures, api.py exposes HTTP endpoints. A new developer can orient herself by reading the file names. When a bug appears in authentication, she knows exactly where to look.

An AI agent is asked to add input validation to a REST API. The project has a validation module with a clear pattern: each endpoint has a corresponding validation schema. The agent follows the pattern, adds the new schema, and wires it in. The module’s structure served as a template the agent could follow.

Tip

When you find yourself writing a code comment like “TODO: move this somewhere better,” that is a signal that the current module boundaries are not right. Respect that signal — it is cheaper to reorganize modules early than to untangle them later.

Example Prompt

“The validation logic is scattered across three files. Create a validation module with a clear pattern: one schema per endpoint. Move the existing validation code into this module and update the imports.”

Consequences

Good module boundaries reduce the mental load of working with a codebase. They give you a map: each module is a labeled region on that map. They support parallel work, so different people (or agents) can work on different modules with minimal coordination.

The downside is that modules impose a taxonomy, and taxonomies can become outdated. When the problem domain shifts, module boundaries may no longer reflect the natural groupings. Renaming, splitting, and merging modules is routine maintenance that too many teams defer.

Sources

  • David Parnas’s “On the Criteria To Be Used in Decomposing Systems into Modules” (Communications of the ACM, 1972) is the founding argument for the modular structure described here. Parnas’s information-hiding criterion — that modules should hide design decisions, not merely partition steps of a computation — is the basis of the public-versus-private boundary the Solution section identifies as “one of the most useful tools in a programmer’s kit.”
  • Edward Yourdon and Larry Constantine introduced the term cohesion (originally coined by Constantine in the late 1960s) and developed it into a usable design metric in Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design (Yourdon Press, 1979). The article’s claim that a module should have “one clear reason to exist, and everything inside it should relate to that reason” is their cohesion principle restated for working programmers.
  • John Ousterhout’s A Philosophy of Software Design (2018; 2nd ed. 2021) reframes modular design around the contrast between deep modules (simple interfaces hiding substantial functionality) and shallow modules (interfaces nearly as complex as their implementations). The Forces section’s tension between “too many small modules” and “too few large modules” maps directly onto Ousterhout’s argument that shallow modules multiply interfaces without paying down complexity.
  • Niklaus Wirth’s “Program Development by Stepwise Refinement” (Communications of the ACM, 1971) established the discipline of decomposing tasks into subtasks and data into data structures as a sequence of design decisions. The view of a module as the unit at which an agent (human or AI) can sensibly take a “modify the validation module” instruction descends from Wirth’s framing of decomposition as the act of choosing where one design decision ends and the next begins.