Engineering

Senior engineers decide without complete information. Here is the method

The skill is not better judgement. It is knowing which kind of door you are standing at.

MobileByteSensei 9 min read
careerdecisionsarchitecture

TL;DR

You will never have complete information. The useful question is not "what else should I find out" — it is what does being wrong cost, and can I take it back?

Reversible decisions should be made fast and alone. One-way doors should be made slowly, with the cost of waiting priced explicitly, and by whoever owns the consequence.

The failure mode is not recklessness. It is treating a one-way door like a reversible one because you are in a hurry, and treating a reversible one like a one-way door because you are nervous. Both are expensive, and the second is more common.

The case this post is about

I had just made a mistake. A publishing script created three duplicate articles on a live publication — three titles that already existed, now existing twice.

They were drafts. Nothing reader-facing, nothing in a feed. But they were real objects on a remote service, and the service in question has no list endpoint, no update endpoint, and no delete endpoint in its API at all. Removing one means driving its web interface by hand.

I had a working delete path. It was measured, it had been used five times before, and it was sitting right there. The decision was whether to use it.

What I did not have: certainty about which of the six posts was which. I had ids and titles. I was confident the three I had just created were duplicates of three that already existed. I had been confident about several things that day already, and had been wrong about most of them.

The four questions that replace a confidence check before an irreversible action — whether the effect can be undone, whether it is outward-facing, what is actually known versus believed, and what a wrong call costs — applied to one delete that was withheld

The symptom

Here is what the state looked like:

"The audit was green, specific, and about the wrong artifact"   8676170b1aa0
"The audit was green, specific, and about the wrong artifact"   91148303a1f8   ← created minutes ago
"It returned an identifier and produced nothing"                fa0353dce096
"It returned an identifier and produced nothing"                62b23a55623c   ← created minutes ago
"A warning is not a refusal, and the driver could not tell"     60ea9bd6112d
"A warning is not a refusal, and the driver could not tell"     d8c21a8667d1   ← created minutes ago

Obvious, isn't it. Three pairs, delete the right-hand column, done in four minutes.

That obviousness is the symptom. The pairs are only obvious if the titles are the thing that identifies a post, and I had spent the afternoon discovering that titles on this platform are not reliable identifiers — the same title can sit on an unlisted draft, a published story, and a stale revision simultaneously.

Why the obvious fix failed

Not that it failed — that it was the wrong question.

"Verify harder before deciding." Appealing, and unbounded. I could have fetched each of the six, compared bodies, checked timestamps, diffed metadata. Each step reduces uncertainty and costs time, and nothing tells you when to stop. "Gather more information" is not a decision procedure; it is a way of postponing one while feeling productive.

"Just delete them, you made them." Probably right. But "probably right" is a different proposition when the action cannot be undone, and I had an unusually good estimate of my own error rate that day: three wrong confident conclusions in about six hours.

"Ask, and wait." Safe, and not free. Waiting has a price — here, a small one. That is the part people skip: they treat waiting as the zero-cost default, so any amount of uncertainty justifies it. Sometimes the cost of waiting is the whole decision.

The three framings share an error. They argue about how sure I was, when the thing that should drive the decision is what happens if I am wrong.

The actual mechanism

Two properties, considered separately, and almost everyone collapses them into one.

Reversibility. If this is wrong, can I undo it, and what does undoing cost? Not "is it theoretically recoverable" — what does recovery actually take.

Blast radius. If this is wrong, who notices? Me, my team, or someone who did not consent to being part of the experiment.

Plot the decision on those two and the answer usually falls out:

A two by two matrix of reversibility against blast radius, with the duplicate-draft deletion placed in the one-way, large-radius quadrant

The deletion sat in the bottom-right. Not because deleting a draft is dramatic — because the undo path did not exist. If I deleted the wrong one, the correct post would be gone from a publication I do not own, and the recovery path was "rewrite and republish, with a new identifier and a new URL."

And here is the asymmetry that actually decided it. Leaving three duplicate drafts in place costs: some clutter, and a note. Deleting the wrong post costs: a lost article and an irreversible change to someone else's property.

The expected cost of waiting was close to zero. The tail cost of being wrong was not. When one side of a decision has a bounded small cost and the other has an unbounded one, the uncertainty barely matters — you take the bounded side even at high confidence, because confidence is the thing you have just demonstrated you cannot calibrate.

The fix

State the door, the radius, and the price of waiting — in that order — and let the structure decide:

DECISION   delete 3 duplicate drafts I created in error
DOOR       one-way — API has no delete; browser-only; no undo
RADIUS     large — a publication I do not own
IF WRONG   a published article is gone; recovery = rewrite + new URL
IF I WAIT  three drafts sit unlisted; cost ≈ zero
CONFIDENCE high — but 3 confident conclusions already wrong today
→ NOT MINE ALONE. Surface it with the ids, do the reversible half now.

The last line matters as much as the verdict. "Not mine alone" is not the same as "stop". The reversible half of the work — adding a gate so the publisher refuses to create a post that a ledger says already exists — had a small radius and a trivial undo, so it went in immediately without asking anyone.

Split the decision and do the reversible part now. Most decisions that feel blocked are two decisions wearing one coat: an irreversible one that needs consent, and a reversible one that is being held hostage to it.

The second half of the fix is what you hand over. "Should I delete these?" is a worse question than the block above, because it makes someone else reconstruct the state. Give them the ids, the door, and the asymmetry, and the decision takes them fifteen seconds.

How we proved it

You cannot A/B a judgement call, so the honest test is narrower: did the frame produce a different action than instinct would have?

Yes, twice, in opposite directions. Instinct said delete them, it is obviously fine — the frame stopped that. Instinct also said you have made several mistakes today, slow down on everything — and the frame explicitly rejected that for the gate, which shipped unreviewed because its blast radius was one repository and its undo was a revert.

That second one is the part I would have got wrong. Fear generalises badly. After a bad call there is a strong pull to treat every subsequent decision as high-stakes, which is how a team ends up reviewing typo fixes while nobody notices the migration.

The gate itself was verified the ordinary way — a deliberately-broken input that must be rejected, run before the fix landed:

$ deno task publish posts/act-and-the-macos-gap/post.md --live
BLOCKED: "act-and-the-macos-gap" was already created on Medium as post e9a31dae9677.

The frame applied to two decisions on the same bad day, producing opposite answers: a working delete path withheld because the action is irreversible on a service with no delete endpoint, and an unreviewed gate shipped because its blast radius was one repository and its undo was a revert

Where the frame breaks

Two cases it handles badly, and both have bitten me.

Reversible, but you will not find out. The matrix quietly assumes you learn you were wrong while undo is still available. A config change that silently halves a cache hit rate is technically a one-line revert and practically a one-way door, because by the time anyone notices, the evidence of what changed is three weeks of noise behind you. The question is not "can this be undone" but "can this be undone by the time I would know to". When detection is slow, treat it as one-way regardless of how easy the revert looks.

Unknown radius. Sometimes you genuinely cannot tell who is downstream — an internal library with no consumer registry, a shared script someone may have automated. The honest move is to treat unknown as large, and the reason is structural rather than cautious: errors in estimating radius are not symmetric. Guessing small when it is large is how you take down something you did not know existed. Guessing large when it is small costs one message.

That asymmetry is the whole frame in miniature. Everywhere a judgement is required, ask which direction the error is cheap in, and lean that way without waiting to feel certain.

What this generalizes to

The phrase "senior engineers are comfortable with ambiguity" is useless advice, because it describes a feeling rather than a method. The method is three questions, and none of them is are you sure.

Can I undo this, and what does undoing cost? Not in principle — in practice, with the tools you have. An API with no delete endpoint turns a routine action into a one-way door, and that property belongs to the platform, not to how big the change feels.

Who pays if I am wrong? Your own repository is one answer. A customer's data, a live publication, someone else's branch is another. The second kind is not yours to decide alone, no matter how confident you are, because confidence is not transferable to the person absorbing the cost.

What does waiting cost? Price it. If the answer is near zero, uncertainty is cheap to resolve and you should resolve it. If the answer is "the deploy window closes", that is a real cost and it belongs in the decision rather than being discovered afterwards as an excuse.

Run those and most decisions stop being hard — not because the uncertainty went away, but because you can see that it was never the deciding factor. The deletion above was not difficult. It only felt difficult while I was trying to become sure, which was the wrong project.

Keep reading

This is part of 100 practices of senior engineers — the habits that separate five years of experience from one year five times. Follow MobileByteSensei for the rest.

One post a week, no digest

New writing when it lands. No roundups, no course pitch, no "10 tips".

Follow by RSS