Skill Tracer
Skills › Skills For Real Engineers › codebase-design › DEEPENING.md

DEEPENING.md

37 lines · skills/engineering/codebase-design/DEEPENING.md at 4ae2317

Open on GitHub
1# Deepening
2
3How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [SKILL.md](SKILL.md): **module**, **interface**, **seam**, **adapter**.
4

Deepening

How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in SKILL.md: module, interface, seam, adapter.

Raw lines 1 to 4
5## Dependency categories
6
7When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
8

Dependency categories

When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.

Raw lines 5 to 8
9### 1. In-process
11Pure computation, in-memory state, no I/O. Always deepenable: merge the modules and test through the new interface directly. No adapter needed.

1. In-process

Pure computation, in-memory state, no I/O. Always deepenable: merge the modules and test through the new interface directly. No adapter needed.

Raw lines 9 to 12
13### 2. Local-substitutable
15Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.

2. Local-substitutable

Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.

Raw lines 13 to 16
17### 3. Remote but owned (Ports & Adapters)
19Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
21Recommendation shape: *"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."*

3. Remote but owned (Ports & Adapters)

Your own services across a network boundary (microservices, internal APIs). Define a port (interface) at the seam. The deep module owns the logic; the transport is injected as an adapter. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.

Recommendation shape: "Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."

Raw lines 17 to 22
23### 4. True external (Mock)
25Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.

4. True external (Mock)

Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.

Raw lines 23 to 26
27## Seam discipline
29- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
30- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.

Seam discipline

  • One adapter means a hypothetical seam. Two adapters means a real one. Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
  • Internal seams vs external seams. A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
Raw lines 27 to 31
32## Testing strategy: replace, don't layer
34- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist; delete them.
35- Write new tests at the deepened module's interface. The **interface is the test surface**.
36- Tests assert on observable outcomes through the interface, not internal state.
37- Tests should survive internal refactors, since they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.

Testing strategy: replace, don't layer

  • Old unit tests on shallow modules become waste once tests at the deepened module's interface exist; delete them.
  • Write new tests at the deepened module's interface. The interface is the test surface.
  • Tests assert on observable outcomes through the interface, not internal state.
  • Tests should survive internal refactors, since they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
Raw lines 32 to 37