design-deep-modules

v2026.09.24

Judge interface depth — deep modules hide complexity behind small interfaces. Use when designing a module/API, reviewing a class boundary, or reducing complexity.

GitHub
Install command
npx skhub add laurigates/design-deep-modules
Markdown
SKILL.md

Design: Deep Modules

From A Philosophy of Software Design (Ousterhout): the best modules are deep — a small, simple interface hiding a large, complex implementation. A shallow module is the opposite: an interface nearly as complicated as the implementation it wraps, so it adds cognitive cost without removing much. This skill assesses a module or interface for depth and points at the cheapest way to deepen it.

The unit of design is complexity at the boundary: every method, parameter, and exception a caller must understand is interface cost; everything the module handles for them is the benefit. Depth is the ratio.

When to Use This Skill

Use this skill when...Use something else instead when...
Designing a new module/class/API surface and choosing what to exposeHunting correctness bugs → code-quality-plugin:code-review
A class boundary "feels" thin — pass-through methods, leaky configCyclomatic/cognitive metrics on a function → code-quality-plugin:code-complexity
Deciding whether to split or merge two modulesRemoving duplication specifically → code-quality-plugin:dry-consolidation
Reviewing whether an abstraction earns its keepRestructuring toward pure functions → code-quality-plugin:code-refactor

Core Principle

Signal of a deep moduleSignal of a shallow module
Few public methods, many privatePublic surface ≈ implementation size
Caller passes intent, not mechanismCaller must orchestrate internal steps
Defaults handle the common caseEvery caller re-specifies the same config
Errors handled or masked internallyExceptions leak the implementation's internals
Name describes what, not howName encodes the mechanism (HashMapUserCache)

Two recurring deepening moves: pull complexity downward (the module, not its callers, absorbs the hard case — a class with N callers pays once, they pay N times) and design it twice (sketch two genuinely different interfaces before committing; the second exposes the first's accidental complexity).

Parameters

Parse $ARGUMENTS:

  • Target (optional, first positional) — a file, module, directory, or diff to assess. If absent, default to the current change (git diff HEAD + staged) and say so.

Execution

Execute this depth assessment:

Step 1: Map the interface vs the implementation

For the target, list the public surface (methods, params, required config, thrown exceptions, ordering constraints a caller must obey) and estimate the implementation weight behind it. State the depth verdict in one line: deep, shallow, or mixed.

Step 2: Locate the shallowness

For each shallow point, name the specific cost the caller pays and why it leaks:

  • Pass-through methods that only forward to another layer (collapse the layer)
  • Config a caller must always set the same way (push a default downward)
  • Internal exceptions surfacing as the public contract (mask or translate)
  • Ordering / temporal coupling ("call init() before run()") (encapsulate)
  • Information leakage — the same design decision known in two modules

Step 3: Recommend the cheapest deepening

Per finding, give the move (pull complexity down / merge the shallow layer / add a default / design-it-twice the interface) and the resulting smaller interface. Prefer one deeper module over two shallow ones only when they share a secret; do not merge modules that are genuinely independent (that is the opposite failure — a god module).

Step 4: Report

Emit: target, depth verdict, the shallow points (ordered by caller cost), and the recommended interface change for each. Keep recommendations behaviour- preserving — this is an interface-shape review, not a rewrite.

Anti-patterns

MistakeCorrect approach
Treating "more, smaller classes" as always betterClassitis makes shallow modules; depth, not count, is the goal
Merging two modules that share no secretThat builds a god module — merge only on shared information
Adding a config knob "for flexibility"Each knob is interface cost paid by every caller; default it
Exposing an exception because it was easyDefine away or mask errors the caller cannot act on

Quick Reference

SymptomDeepening move
Pass-through / wrapper methodCollapse the layer
Repeated caller configPush a default downward
Leaky exceptionsMask or translate at the boundary
"Call A before B"Encapsulate the sequence
Two interfaces both plausibleDesign it twice, pick the smaller surface

Related

  • code-quality-plugin:code-complexity — function-level cyclomatic/cognitive metrics (this skill works at the interface level above that)
  • code-quality-plugin:code-refactor — the mechanical moves that execute a deepening once it's chosen
  • code-quality-plugin:dry-consolidation — information leakage is often duplication; dedupe is one deepening move
  • software-design-plugin:design-by-contract — what the deepened interface should promise its callers
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

software-design-plugin/skills/design-deep-modules

Default branch

main

Latest commit

1668324

Tree SHA

b2d4cc3