Software Module Decomposition¶
Architectural decomposition — instantiates Modular Decomposition
Splits a codebase into modules whose internals are hidden behind published interfaces, so each can be changed without reopening the rest.
Software Module Decomposition carves a codebase into packages, services, libraries, or bounded contexts whose defining move is information hiding: each module publishes a stable interface and locks its implementation, data structures, and design secrets behind that interface, so the rest of the system depends only on what the module promises, never on how it keeps that promise. The unit of decomposition is not the file or the folder — it is the secret: a decision likely to change is sealed inside one module so that when it does change, only that module is edited. What makes this THIS mechanism, rather than merely "code in different directories," is that the boundary changes the dependency graph, not just the directory tree; a rearrangement that leaves every module still reaching into every other module's internals has decomposed nothing.
Example¶
A team owns a monolithic order-processing service where the tax calculation, the currency formatting, and the fraud check all read and mutate the same shared Order object directly. A rate change in one tax jurisdiction means grepping the whole codebase for anything that touches tax, because nothing hides it. They decompose. Tax becomes a module with one published function — computeTax(cart, jurisdiction) -> TaxResult — and everything about how tax is computed (the rate tables, the rounding rules, the jurisdiction lookup) moves behind that boundary. The Order object stops being a shared mutable free-for-all; other code now receives a TaxResult rather than reaching into tax internals.
The payoff shows up the next time a jurisdiction changes its rounding rule. Before, that was a system-wide hunt. After, it is a one-file change inside the tax module, and the interface — computeTax still returns a TaxResult — does not move, so nothing downstream recompiles or re-tests. The decomposition is real precisely because the change stayed local.
How it works¶
The distinguishing method is choosing boundaries by anticipated change, not by function name:
- List the secrets. Enumerate the decisions most likely to change independently — a data format, an algorithm, an external vendor's API. Each such secret is a candidate module.
- Draw the boundary around the secret. Everything that would have to change together when that decision changes goes inside; everything else stays out. This is what makes coupling low and cohesion high, rather than the reverse.
- Publish a narrow interface. Expose the smallest surface that lets callers do their job — a function signature, an API endpoint, a message contract — and make it the only legal way in.
- Enforce the seal. Access modifiers, module systems, or architectural-fitness tests forbid callers from reaching around the interface into internals.
Tuning parameters¶
- Interface width — how much the module exposes. A narrow interface hides more and decouples harder, but forces callers through a stricter contract; a wide one is convenient today and a coupling liability tomorrow.
- Module size — one large module per bounded context or many small ones. Bigger modules cut cross-module calls but grow internally entangled; smaller ones localize change but multiply the wiring.
- Physical vs. logical boundary — whether the module is a separate deployable service or an in-process package. A service boundary enforces the seal at runtime but adds network cost and failure modes; a package boundary is cheap but only as strong as the team's discipline.
- Contract strictness — versioned, backward-compatible interfaces vs. free-to-break internal ones. Strictness protects callers but slows the module's own evolution.
When it helps, and when it misleads¶
Its strength is that it makes change local: when the hidden decision changes, one module is edited and the interface holds, so the blast radius is the module rather than the system. This is the original argument for decomposing by information hiding rather than by flowchart step.[1] It also lets teams reason about, test, and replace a module knowing only its contract.
It misleads when boundaries are drawn around the wrong axis. The classic failure is decomposing by technical layer — all controllers here, all data-access there — which feels tidy but scatters every real change across all layers, so no change is local; and the fashionable version is splitting a monolith into microservices along the same bad seams, producing a distributed monolith whose modules cannot change independently but now fail over the network too. The guarding discipline is to test each candidate boundary against a real change scenario ("when the tax rule changes, does exactly one module change?") before committing, and to keep the interface narrow enough that callers cannot quietly grow a dependency on an internal detail.
How it implements the components¶
module_boundary— the boundary is drawn around a design secret: what varies independently goes inside, everything else stays out.encapsulation— its defining act; implementation, data, and design decisions are sealed behind the interface so they never become system-wide concerns.interface_contract— the published API (function signature, endpoint, message schema) is the sole legal dependency surface between modules.
It does not size subsystems or run integration tests (module_granularity, integration_policy — that's Product Subsystem Decomposition) or assign human owners (module_steward, coordination_protocol — that's Organizational Team Boundaries). Its nearest twin is Mechanical Subassemblies, which shares boundary-and-interface machinery but turns on physical fit (compatibility_check) where this one turns on hidden design secrets.
Related¶
- Instantiates: Modular Decomposition — the software-specific realization, where the module's "secret" is a design decision sealed behind an API.
- Sibling mechanisms: Organizational Team Boundaries · Product Subsystem Decomposition · Curriculum Units · Legal or Policy Sections · Mechanical Subassemblies
Editorial Notes¶
Form Classification¶
Form family: Structure, Architecture & Configuration
Rationale: Software Module Decomposition operates as a configured physical, technical, or logical arrangement whose structure creates the effect because it splits a codebase into modules whose internals are hidden behind published interfaces, so each can be changed without reopening the rest.
Independent corroboration: The frozen evidence defines Software Module Decomposition as 'Splits a codebase into modules whose internals are hidden behind published interfaces, so each can be changed without reopening the rest', so its operative form is Structure, Architecture & Configuration.
Nearest alternative: Intervention, Treatment & Transformation — Software Module Decomposition includes features of a direct treatment or transformation applied to a target to change its state or condition, but its defining operation is a configured physical, technical, or logical arrangement whose structure creates the effect.
Review outcome: Independent reviewer agreement; medium confidence.
Origin Attribution¶
Primary origin: Computer Science & Software Engineering
Origin pattern: Single lineage
Present-day reach: Multi-domain
Rationale: Hiding module internals behind published interfaces to enable independent change is canonical modular software design.
Related originating lineages:
- Engineering & Design — Physical modularity and interface control provide a broader design tradition.
- Organizational & Management Science — Module boundaries often align with accountable team ownership.
- Systems Thinking & Cybernetics — Decoupled subsystems limit change propagation.
Review resolution: The blind reviewers agree that computer_science is the primary origin and differ only on alternate origin disagreement, domain reach disagreement. I preserve every independently explained alternate from both records rather than imposing a numeric cap. I retain single_lineage because the combined evidence shows one traceable formative lineage. The broader reach of multi_domain records portability separately from historical provenance; encyclopedia_synthesis=false preserves the affirmative synthesis judgment where either reviewer identified one.
Review outcome: Reconciled after independent review; high confidence.
References¶
[1] David Parnas, On the Criteria To Be Used in Decomposing Systems into Modules (1972), argued that modules should be organized around design decisions likely to change — "information hiding" — rather than around steps in the processing flowchart. It remains the canonical justification for boundary-by-secret over boundary-by-function. registry ↩