R1.17.2indirection cost of reusedesign

Extra indirection added for reuse raises the cost of understanding

Aliases: wrapper hell · indirection tax · Base-star wrappers

What it is

To make two call sites “share one implementation”, real controls get wrapped in Base*, withX, render proxies, data bridges. Each layer pushes “the thing that actually paints and actually handles the keyboard” one hop further down. Indirection cost of reuse is the number of hops required to understand one call: from the product file through a shell, through a generic list, through a cell, to the actual button. Reuse saves duplicated implementation lines; it spends them on rebuilding that chain on every edit and every debug. When hops cost more than the savings, indirection is buying lines with understanding, and usually losing.

Indirection is not the depth of the product tree. A page with a heading and a list is duty-depth. Indirection is the layers that do no work of their own and only rename parameters on the way down.

Why it happens

Working memory holds a limited number of bindings at once. Each extra rename (items become data become rows) adds a correspondence table the reader must keep. Debugging, the stack walks those layers: a breakpoint on the shell cannot see the real click handler; a breakpoint on the kernel cannot see what the product passed. Reuse pays most at the first shared site; each layer after that often does not grow the shared set, it only makes existing sharing more “flexible” — flexibility is more branches, and branches become new indirection.

Indirection also cuts search. Looking for “how is the primary action button used in the product”, a search for the component name hits only the shell; the real button’s name is hidden inside the library. Docs and types tend to be written on the outermost layer; the kernel’s constraints never reach the call site. The call looks short because complexity was pushed somewhere unreadable.

Where it stops holding

A platform adapter (the same semantics onto Web and native) is indirection worth paying, because the two kernels are not one codebase. Permissions and experiment flags injected by infrastructure beat every call site wiring them itself. A single hop, a correspondence table of two or three stable keys, and a kernel never referenced from product: the cost is negligible. If the extra layer is exposed as public API, cost multiplies again — consumers now learn two names. Three layers laid down “for reuse later” while there is still one call: understanding cost is paid in full, reuse yield is zero.

Applying it

  • Draw the call chain: from the product file to the file that actually handles click / focus. Mark red any layer that only renames and adds no constraint. More than one red layer: delete before talking reuse.
  • Forbid Base* / withX that exist only to rename. To keep one, that layer must add a stateable constraint (validation, accessibility synthesis) and expose it in types, not hide it in the kernel.
  • A search for the real control’s name must hit from the product repo; if it cannot, the name has been eaten by indirection. Lift the kernel name to public, make the shell a thin alias, schedule its deletion.
  • How to check: ask someone who did not write the chain to follow a click in the debugger to the handler; count hops and renames. More than two renames and the handler is still missing: indirection already costs more than the lines saved. Print types of the outermost layer and the kernel: each mismatched key is a correspondence-table entry that must stay in working memory. Delete one red layer: if both product calls still work and hops drop, that is the reuse worth keeping.

Related

  • Same group: R1.17.1 The more generic a component's name, the easier it is to dump unrelated duties into it · R1.17.3 Inline duplication is sometimes cheaper than the wrong abstraction
  • Adjacent: R1.08 Premature abstraction · R1.06 Contribution and governance
  • Search terms: indirection cost of reuse · wrapper hell · indirection tax

Cards in the same group

Quick Actions

Share

Share this page

ios_share

https://hci.top/en/handbook/R1.17.2