Engineering Economy
When to use this
Every "how should we do this" decision. The cost of engineering is not only writing the thing; it is the cost of changing it, the cost of verifying it, and the cost of it being wrong later. This skill is a set of rules that make four things true at once: fewer changes, better features, less test time, and completeness without errors — so the effort you save goes into building more of what matters.
1. Fewer changes (a smaller blast radius is easier to verify and harder to break)
Rule 1: Prefer riding an already-verified path over building a parallel mechanism
Before building something new, ask "which existing path has already done something similar, and has already been verified?" — then ride it instead of laying a parallel track. A new path is a new unverified surface: a new source of errors, plus a whole new verification apparatus to build. Riding a verified path means the new feature inherits its correctness guarantees almost for free.
★"Already-verified paths" include the outside world's (check external experience before starting): when you hit a wall, what you hit is very likely a known property that everyone has hit (example: a mainstream library's official documentation states outright that a normal load consumes roughly 50 times the file size in memory — three rounds of OOM head-butting, and one look at the docs solved it, because the right answer, streaming mode, was already written down for you). Checking official docs, mature libraries, and papers before starting or when stuck costs minutes and saves rounds of re-collision. But borrow without copying: (a) before adopting, test it with your own oracle (external libraries are wrong sometimes too; open source is not a reason to trust); (b) spell out the delta layer you are adding on top (what extra guarantees your situation needs); (c) recognize the hidden traps in their general solution (a "persistence" implemented as whole-object serialization puts the problem right back where it was). Borrow the foundation, build your own walls.
★The time to look outward is before you start, not after you hit the wall: "look it up when you hit a problem" is firefighting research — you pay the tuition first and read the blueprint afterwards, when the detour is already behind you. The right sequence is: before starting any new block (anything that requires inventing a new mechanism, data flow, or interaction pattern), the first deliverable is a half-page external benchmark summary: (a) the industry-standard approach (cross-checked across 2-3 sources), (b) what you will follow, (c) the delta layer where you intend to do better, (d) where you knowingly deviate and why. The summary passes review before you write the first line of code. Bug fixes and iteration inside an existing mechanism need no re-research. A real counter-example from the same project: the data layer did its external research before starting and stood on the standard the whole way; the interface layer skipped it and improvised, and hit tab state, task resurrection, and reconnect semantics one landmine at a time — and afterwards, a single search showed every one of those standard answers already printed in the industry literature. Same people, same week; the only difference was when they looked.
Rule 2: Make surgical edits; do not rewrite whole sections
When changing existing code, make the smallest incision: change only what must change, and do not
rewrite the surrounding section while you are in there. A rewrite turns something already verified back
into something unverified, and the risk and verification cost all start over. "If one if solves it,
do not refactor the whole function."
Rule 3: Freeze the data contract before consumers can depend on it
When a piece of data or an interface will be consumed by another module or another stage, pin its schema first, then build downward.
- Consumers integrate against that contract; a late contract means consumers have nothing to build against, or build something and then rework it.
- Especially for things "produced in this batch, consumed in the next": freeze the structure and label it honestly as "produced, awaiting consumption," and the next batch connects directly instead of re-litigating it (a decision made does not evaporate).
Rule 4: For interface work, ship a static prototype for sign-off before writing code
When building "something a person will look at" (a UI, a report layout, a conversational flow), first build a static prototype (a mock with no backend) and get the decision-maker to settle the shape — the look, the copy, the flow. Write the real code only after sign-off. The reason: interface rework is the most expensive kind (code, styling, and wiring all move together), so "is this right?" should be answered at the cheapest possible stage.
2. Less test time (verification is a tax you pay every time; drive it down)
Rule 5: The economics of test tiering
Tier by "how fast × how often":
- Second-scale smoke: runs on every change, verifying only that the critical things are alive.
- Minute-scale targeted: verify what you changed (via dependency tracking), running only what this change affected.
- Full: slow, exhaustive, run once at the merge gate (schedulable off-peak). Day to day, the first two tiers keep you fast; the full tier's cost is amortized at the gate. (This covers why tiering saves time and how the cost is computed; for what correctness each tier is responsible for, see the sister skill verification-discipline and its tiered verification. The cost side and the correctness side are only complete read together.)
Rule 6: Each guarantee lives in exactly one layer; never verify the same thing twice
Do not verify one guarantee once per layer — duplication is cost paid for nothing. The division of labor:
- Unit layer: verifies deterministic logic (given this input, exactly this output).
- Integration layer: verifies the wiring connects (the stages compose and run).
- Real-environment layer: spot-checks only (a real environment is expensive and slow; prove the critical path connects, and do not repeat what the unit layer already guarantees).
Rule 7: Put injected doubles in the regression gate for non-deterministic dependencies; sample the real thing selectively
The network, AI models, the clock, randomness — put these non-deterministic dependencies straight into an automated regression gate and it will fail randomly, and every phantom failure costs investigation and a re-run. Over time that is enormous. Instead:
- Inside the regression gate, use injected doubles (mock/stub/fake): deterministic, no network, reproducible.
- Exercise the real version only as a selective sample (opt-in, skipped when there is no key or no network), never as a gate that must run every time.
- The payoff: the regression gate is deterministically green forever, with non-determinism kept outside the door.
Rule 8: Isolate tests to stop phantom stalls from burning time
Tests must not interfere with each other — each gets its own temporary files and state, cleaned up afterwards. The cost of getting this wrong is very concrete: a leftover old process holding a file lock, the previous round's state polluting the next → phantom stalls and phantom failures, each costing tens of minutes of investigation. Isolation done up front is a one-time investment with a long payoff. (For handling a phantom stall in the moment — kill the interfering process precisely by PID, re-run clean, take the green — see handoff-protocol and its "phantom stall vs. real failure.")
Rule 9: Reconcile exactly, so "targeted" is trustworthy
What entitles you to run only the targeted tests (rather than the full suite every time) is trusting that the targeted set covered what it should. Exact reconciliation is what holds that up: "baseline N + M new = N+M" — the numbers add up, and only then may you say "this change only needs these verified." Loose reconciliation → you can only fall back on running everything every time, and you save nothing.
3. Completeness without errors (the cost you saved must not be paid in correctness)
Rule 10: Every test states its scope explicitly — what it proves and what it does not
A test should honestly state what it proves and what it does not. False safety is more dangerous than no safety — the part you believed was verified and wasn't is exactly where the next incident comes from. "This proves the computation is right; it does not prove the frontend is wired" — write that down.
Rule 11: Failure modes lean to the safe side, because that blocks the most expensive cost
Saving cost must never push risk toward being silently wrong. Leaning safe looks like "spending a little more," but what it blocks is the most expensive cost of all — a silent error flowing downstream, blowing up much later, and being the most expensive thing to trace. The price of leaning safe (one extra question, one extra block) is immediate, tiny, and visible. Trading a small visible cost for a large invisible one is always a good trade. (For how to lean safe in practice — treat the undecidable as not passing, hand uncertain extraction back to a human — that is behavioral, and the authoritative version lives in the sister skill verification-discipline under failure modes leaning safe; here we only cover why it is not a loss in cost terms.)
Rule 12: Performance is held down by assertions, not by claims
"This way is faster / uses less memory" is a claim; a baseline assertion inside the regression gate is a guarantee:
- Record a baseline for both elapsed time and peak memory (RSS), and assert ≤ baseline × a tolerance factor (1.5, for instance); over that is a FAIL, not a warning.
- Baseline one-time cost and steady-state cost separately (one line for cold, one for warm) so neither hides the other (for the judgment principle, see verification-discipline Rule 3).
- Pair it with breaking tests: deliberately fall back to the slow path → it must bark; deliberately use a memory-exploding formulation (materializing everything) → it must bark. A gate whose pre-check barks is a gate.
- How the ledger works: a performance regression caught by an assertion pre-merge costs minutes; discovering "why did this get slow / why did this blow up" in production costs investigation, a rollback, and trust. Write the baseline number only once it is measured (a number pulled from the air is fake precision); the moment you have measured it, put the gate up.
Case files from this project (supporting evidence, not required for the general rules)
- Riding a verified path (Rule 1): the third batch's "adopt a distilled candidate" rode directly on the just-verified "generate code → verify → enqueue" track rather than building a new landing mechanism — the new feature inherited that track's correctness for free, with zero new safety machinery.
- Freezing the contract (Rule 3): "this batch only captures the decision; the next one executes the merge" — the decision-capture file's schema was pinned first and labeled "decided, awaiting execution," so the next batch's consumer connected directly with nothing to re-ask (echoing the earlier "pin the schema first" lesson).
- Prototype first (Rule 4): the conversational flow shipped a static mock first so a human could settle the wording and the card layout, and the code came after sign-off — reusing the earlier successful pattern of "mock the four-field confirmation card first."
- Injected doubles in the gate (Rule 7): AI and network verification use a MockProvider injected into the regression gate (deterministic); real network calls are opt-in samples only, skipped without a key, and never a gate.
- Isolation against phantom stalls (Rule 8): a leftover test process once fought over a Windows file lock and produced a phantom stall that burned investigation time; fixing test isolation (temporary files, monkeypatched file paths) cured it at the root.
- Tiering + reconciliation (Rules 5/9): three regression tiers plus a per-batch "baseline + new tests" reconciliation let day-to-day work run only the targeted tier, with the full suite amortized at the merge gate.
- External experience as the answer (Rule 1, extended): whole-sheet loading of a large file blew up with OOM three rounds running; one look at the official documentation — "a normal load consumes roughly 50 times the file size in memory," in black and white, with the official answer being streaming mode. The same round, external research also headed off a hidden trap (implementing the "persisted lookup table" as whole-object serialization → loading it back rebuilds everything → the peak returns), which became on-disk point lookups instead. Check external sources first, and three rounds of head-butting become one look.
- Performance assertions in the gate (Rule 12): the large-file cache-seeding flow wrote "elapsed baseline × 1.5 + peak RSS baseline × 1.5 + two breaking tests (fall back to the slow path → barks / materialize the whole sheet → barks)" into the release-gate rules, with cold and warm listed separately; the baseline values were written only once a real batch measurement settled them, never pulled from the air.