Files
Gabriel Brown 89761a7da3 Keep the personal half of the desktop in one place, and ask before installing it
Agent instructions, skills, SSH host aliases and expansion triggers are worth
having identical on every machine one person owns, and belong in none of the
shared configuration. They live in user/ now, with a manifest saying where each
piece goes and a link-user stage that puts it there.

That stage does nothing unless the machine said yes. Somebody who clones Panama
to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was;
the question names the destinations and defaults to no. Anything displaced goes
to config/old rather than being deleted.

~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one
file, which is the drift this exists to prevent.

Also adds the vitals toggles for the battery and Claude usage readouts, which
had preferences and no way to reach them.
2026-08-22 08:54:43 -04:00

6.5 KiB

name, description
name description
codebase-design Use when designing or changing a module's interface, deciding where a seam goes, judging whether code is too shallow to be worth its surface, making something testable, or when another skill needs the deep-module vocabulary.

Codebase Design

Design deep modules: a lot of behaviour behind a small interface, placed at a clean seam, testable through that interface. The aim is leverage for callers, locality for maintainers, and testability for everyone. This is the reference for that language; it is consulted, not run.

Glossary

Use these terms exactly. Do not substitute "component", "service", "API", or "boundary": the consistent language is the whole point, because a shared word is what lets a design discussion stay about the design.

Module: anything with an interface and an implementation. Deliberately scale-agnostic: a function, a class, a package, or a tier-spanning slice. Avoid: unit, component, service.

Interface: everything a caller must know to use the module correctly. The type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics. Avoid: API, signature, which are too narrow because they name only the type-level surface.

Implementation: what is inside a module, its body of code. Distinct from adapter: a thing can be a small adapter with a large implementation (a Postgres repository) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic, "implementation" otherwise.

Depth: leverage at the interface. The amount of behaviour a caller or a test can exercise per unit of interface it has to learn. A module is deep when a large amount of behaviour sits behind a small interface, shallow when the interface is nearly as complex as the implementation.

Seam (Michael Feathers): a place where you can alter behaviour without editing in that place; the location at which a module's interface lives. Where to put the seam is its own design decision, separate from what goes behind it. Avoid: boundary, which is overloaded with DDD's bounded context.

Adapter: a concrete thing that satisfies an interface at a seam. Names a role, the slot it fills, not its substance.

Leverage: what callers get from depth. More capability per unit of interface learned. One implementation pays back across N call sites and M tests.

Locality: what maintainers get from depth. Change, bugs, knowledge, and verification concentrate in one place instead of spreading across callers. Fix once, fixed everywhere.

Deep vs shallow

A deep module is a small interface over a large implementation:

┌─────────────────────┐
│   Small interface   │  ← few entry points, simple params
├─────────────────────┤
│                     │
│ Deep implementation │  ← complex logic hidden
│                     │
└─────────────────────┘

A shallow module is a large interface over a thin implementation, and is the thing to avoid:

┌─────────────────────────────────┐
│        Large interface          │  ← many entry points, complex params
├─────────────────────────────────┤
│  Thin implementation            │  ← mostly passes through
└─────────────────────────────────┘

When designing an interface, ask: can I reduce the number of entry points, simplify the parameters, or hide more complexity inside?

Principles

  • Depth is a property of the interface, not the implementation. A deep module can be internally composed of small, swappable parts; they simply are not part of the interface. A module can have internal seams, private to its implementation and used by its own tests, as well as the external seam at its interface.
  • The deletion test. Imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
  • The interface is the test surface. Callers and tests cross the same seam. Wanting to test past the interface means the module is probably the wrong shape.
  • One adapter means a hypothetical seam. Two adapters means a real one. Introduce a seam only when something actually varies across it.

Designing for testability

Good interfaces make testing natural.

Accept dependencies, do not create them:

// Testable: the seam is a parameter
function processOrder(order: Order, gateway: PaymentGateway) {}

// Hard to test: the dependency is welded in
function processOrder(order: Order) {
  const gateway = new StripeGateway();
}

Return results, do not produce side effects:

// Testable: the outcome is the return value
function calculateDiscount(cart: Cart): Discount {}

// Hard to test: the outcome is a mutation
function applyDiscount(cart: Cart): void {
  cart.total -= discount;
}

Keep the surface small. Fewer entry points mean fewer tests; fewer params mean simpler setup.

Relationships

  • A module has exactly one interface, the surface it presents to callers and tests.
  • Depth is a property of a module, measured against its interface.
  • A seam is where a module's interface lives.
  • An adapter sits at a seam and satisfies the interface.
  • Depth produces leverage for callers and locality for maintainers.

Rejected framings

  • Depth as the ratio of implementation lines to interface lines (Ousterhout): rewards padding the implementation. Depth-as-leverage is the definition used here.
  • "Interface" as the TypeScript interface keyword, or a class's public methods: too narrow. The interface includes every fact a caller must know, including the ones the types cannot carry.
  • "Boundary": overloaded with DDD's bounded context. Say seam or interface.

Going deeper

  • Deepening a cluster given its dependencies: DEEPENING.md covers the dependency categories, seam discipline, and replace-don't-layer testing.
  • Exploring several interfaces for one module: DESIGN-IT-TWICE.md covers the parallel design pattern, and the gate for when it is worth the ceremony.