Build the alternative before you choose
Purpose
In someone else's repo, a design choice defended by reasoning invites the reviewer
to re-do the reasoning; a choice defended by a measurement of the built
alternative gives them nothing to re-litigate. The same applies inward: "out of
scope", "upstream's call" and "arguably correct" are the three phrases that most
often mark an assumption wearing a decision's clothes. Each is a claim, and claims
about buildable things get verified by building. The pattern that makes this cheap:
most alternatives are a small patch away once the main design exists — the expensive
part (harness, tests, invariants) is already built and serves both.
When to use
- You are about to write "arguably", "probably fine", "the alternative would be a
bigger design", or "that is their decision" in a design note, PR body, or handoff.
- A semantics trade has two coherent sides (streaming vs batching, eager vs lazy,
fail vs warn) and you picked one by taste.
- A reviewer could ask "why not the other way?" — pre-empt with the other way's
measured numbers, not adjectives.
When NOT to use
Decisions that are literally someone else's authority (whether a public API ships at
all, licensing, release timing) — there, build the evidence package, not the
decision. And don't build alternatives to requirements nobody traced to a real need:
first check the demand, or you measure two answers to a question no one asked.
The practice (checklist)
Rationalizations
| Shortcut |
Why it fails |
| "The alternative is obviously worse, why waste an hour?" |
Ours wasn't obviously worse — it won one measured case (batching a double-triggered source). The hour bought the real shape of the trade: it also serialized every join behind unrelated work and broke four pinned tests. "Obvious" had the win/loss split wrong. |
| "It's a bigger design, we'd be building for weeks." |
The main design's harness already existed; the "bigger design" was a 7-line patch plus one probe run. The size estimate was the assumption, and it inflated exactly like the "three-package change" that turned out to be two packages and four lines. |
| "That's the maintainer's call, we shouldn't decide." |
The maintainer decides whether it ships; you still owe the measured comparison they will decide on. Arriving with two built variants and a table is help; arriving with an open question is homework. |
| "We measured our variant thoroughly, that's enough." |
A 2,400-seed fuzz proved the implementation matched the chosen rule; nothing in it asked whether the rule was right. Verification depth cannot substitute for a comparison — an inverted evidence pyramid (deep verification on a shallow justification) is the tell. |
| "Out of scope — a process kill / other runtime / race can't be tested here." |
Each of those fell within a day of being attempted: a killed child process with a file-backed saver, a 40-line prototype in the other runtime, a two-invoke race. "Can't be tested" usually means "haven't tried building the rig". |
RECEIPT
SOURCING-receipt, internal (pending publication of the branch it guarded).
2026-08-14, langgraphjs inclusive-waiting-edge work. The once-per-arming release
semantics stood in the record as "arguably correct — the alternative is a different,
bigger design" until the rule was applied. The alternative (quiescence-only,
batching) was built as a 7-line patch reusing the existing harness and measured side
by side on six cases: it won the double-trigger case (1× vs 2×), lost the
parallelism case (the join serialized behind an unrelated slow branch — precisely
the cost of the already-documented workaround, which would have left the feature
without a reason to exist), and carried a four-test integration bill. The same rule
that day converted three "out of scope" lines into measurements: a kill -9
crash-recovery run, a Python port passing every red-team case, and a same-thread
concurrency race that measured as base behavior. Patch and side-by-side probe
archived; the decision is reversible for the price of one file.
Lifecycle
Record in the LEDGER each time the built alternative changed the decision versus
confirmed it — if it only ever confirms, the trigger-word list is catching the wrong
sentences. Sharpen the trigger list with each phrase that later turned out to hide
an unbuilt assumption.
1---2name: build-the-alternative-before-you-choose3description: When a design decision is defended in prose — "the alternative is a bigger design", "arguably correct", "that's out of scope", "that's the maintainer's call" — build the alternative (or measure the deferred claim) before recording the choice. A rejected-by-argument option costs a paragraph and settles nothing; a rejected-by-measurement option costs an hour and ends the debate, including the future re-litigation in review. Words that trigger this: arguably, probably, bigger design, out of scope, their call, we assume. Use when you are about to record a design choice whose alternative was rejected in prose rather than measured, or to defer a claim to someone else's judgement. Trigger terms: design trade-off, alternative considered, out of scope, arguably, defer to maintainer, prototype, spike, measured choice.4---56# Build the alternative before you choose78## Purpose910In someone else's repo, a design choice defended by reasoning invites the reviewer11to re-do the reasoning; a choice defended by a measurement of the *built*12alternative gives them nothing to re-litigate. The same applies inward: "out of13scope", "upstream's call" and "arguably correct" are the three phrases that most14often mark an assumption wearing a decision's clothes. Each is a claim, and claims15about buildable things get verified by building. The pattern that makes this cheap:16most alternatives are a small patch away once the main design exists — the expensive17part (harness, tests, invariants) is already built and serves both.1819## When to use2021- You are about to write "arguably", "probably fine", "the alternative would be a22 bigger design", or "that is their decision" in a design note, PR body, or handoff.23- A semantics trade has two coherent sides (streaming vs batching, eager vs lazy,24 fail vs warn) and you picked one by taste.25- A reviewer *could* ask "why not the other way?" — pre-empt with the other way's26 measured numbers, not adjectives.2728## When NOT to use2930Decisions that are literally someone else's authority (whether a public API ships at31all, licensing, release timing) — there, build the *evidence package*, not the32decision. And don't build alternatives to requirements nobody traced to a real need:33first check the demand, or you measure two answers to a question no one asked.3435## The practice (checklist)3637- [ ] **Catch the trigger words in your own text**: arguably / probably / bigger38 design / out of scope / their call / we assume. Each one names a buildable39 experiment or it stays out of the record.40- [ ] **Reuse the main design's harness**: implement the alternative as the smallest41 patch that changes only the disputed rule (ours was 7 lines on one channel42 class), switchable by env var or applied/reverted patch.43- [ ] **One case set, both variants, side by side** — including the case each side44 is expected to win. If your comparison has no case the alternative wins,45 you built a strawman.46- [ ] **Let the pinned tests vote too**: run the full suite under the alternative47 and read the failures as its integration bill, not as noise.48- [ ] **Record the loser with its numbers and archive the patch** — the choice stays49 reversible for the price of one file, and the review answer to "why not X?"50 is a table.51- [ ] Same discipline for "out of scope" lines: a crash test, a port to the other52 runtime, a concurrency race — each is usually one probe away. Build it or53 strike the line.5455## Rationalizations5657| Shortcut | Why it fails |58|---|---|59| "The alternative is obviously worse, why waste an hour?" | Ours wasn't obviously worse — it *won* one measured case (batching a double-triggered source). The hour bought the real shape of the trade: it also serialized every join behind unrelated work and broke four pinned tests. "Obvious" had the win/loss split wrong. |60| "It's a bigger design, we'd be building for weeks." | The main design's harness already existed; the "bigger design" was a 7-line patch plus one probe run. The size estimate was the assumption, and it inflated exactly like the "three-package change" that turned out to be two packages and four lines. |61| "That's the maintainer's call, we shouldn't decide." | The maintainer decides *whether it ships*; you still owe the measured comparison they will decide on. Arriving with two built variants and a table is help; arriving with an open question is homework. |62| "We measured our variant thoroughly, that's enough." | A 2,400-seed fuzz proved the implementation matched the chosen rule; nothing in it asked whether the rule was right. Verification depth cannot substitute for a comparison — an inverted evidence pyramid (deep verification on a shallow justification) is the tell. |63| "Out of scope — a process kill / other runtime / race can't be tested here." | Each of those fell within a day of being attempted: a killed child process with a file-backed saver, a 40-line prototype in the other runtime, a two-invoke race. "Can't be tested" usually means "haven't tried building the rig". |6465## RECEIPT6667*SOURCING-receipt, internal (pending publication of the branch it guarded).*682026-08-14, `langgraphjs` inclusive-waiting-edge work. The once-per-arming release69semantics stood in the record as "arguably correct — the alternative is a different,70bigger design" until the rule was applied. The alternative (quiescence-only,71batching) was built as a 7-line patch reusing the existing harness and measured side72by side on six cases: it won the double-trigger case (1× vs 2×), lost the73parallelism case (the join serialized behind an unrelated slow branch — precisely74the cost of the already-documented workaround, which would have left the feature75without a reason to exist), and carried a four-test integration bill. The same rule76that day converted three "out of scope" lines into measurements: a kill -977crash-recovery run, a Python port passing every red-team case, and a same-thread78concurrency race that measured as base behavior. Patch and side-by-side probe79archived; the decision is reversible for the price of one file.8081## Lifecycle8283Record in the LEDGER each time the built alternative *changed* the decision versus84confirmed it — if it only ever confirms, the trigger-word list is catching the wrong85sentences. Sharpen the trigger list with each phrase that later turned out to hide86an unbuilt assumption.