Files
vac-optimizer/.agents/skills/codebase-design/DEEPENING.md
T
2026-09-25 10:00:00 +02:00

2.7 KiB

Deepening

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

Dependency categories

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

1. In-process

Pure computation, in-memory state, no I/O. Merge only when it concentrates a cohesive responsibility and reduces caller complexity. Test through the resulting behavior-level interface; no adapter is needed for pure computation.

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.

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."

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.

Seam discipline

  • Justify a seam by its current benefit. Test isolation, ownership, real variability, or an external contract can justify it; adapter count alone cannot. Do not manufacture a second implementation or an unused port.
  • 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.

Testing strategy: preserve behavioral coverage

  • Before deleting old tests, map their success, failure, and edge-case coverage to the replacement checks. Delete only redundant or obsolete tests after confirming equivalent meaningful coverage; keep tests that catch distinct failures.
  • 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.
  • Prefer tests that survive behavior-preserving refactors. A test change is a signal to review coupling, not proof that the test is worthless; integration, algorithm, and concurrency checks may protect distinct contracts.