Single Source of Truth in Python — One Canonical Decision Function, Fail-Safe Defaults, and Why Forked Logic Silently Drifts
The worst bug I shipped this year wasn’t wrong code. It was two pieces of code that were both right, and disagreed.
I’m a self-taught operator. No CS degree. I run a real California field-services business on software I wrote myself, and I spent today doing surgery on a class of bug that almost nobody warns you about. Not a crash. Not a typo. A second opinion. Two functions in my system that each decided the same fact — and over time, quietly, drifted apart. One said a job was finished. The other had never heard about it. Both passed their tests. The customer is the one who found out.
So today was a consolidation day. I went hunting for every place my system held two opinions about one thing, and I collapsed them into one. Then I made the one place fail toward safety when it wasn’t sure. Let me show you the bug first, because you have to feel it before the fix means anything.
// EXHIBIT A — two functions, one fact, slow divergence
Read both halves. Path B writes the real-world result into a field — a human-shaped, multi-line string with a note and a timestamp. Path A checks whether the job is done by asking outcome == "completed". An exact-string match. Lowercase. No note. No newline.
They never agree. Path B writes "COMPLETED — left with…" and Path A is looking for the literal word "completed", so the equality is False forever. The job gets marked done in one place and sits frozen in the other. Nothing throws. The log even says “outcome recorded.” The completion email that’s supposed to fire on advance? Never sends. The client waits, then emails me asking why they heard nothing, and I’m the guy explaining that yes, it’s done, my software just forgot to tell anyone.
Here’s the part that should scare you: both functions are individually correct. Path B writes a faithful record. Path A’s equality check is valid Python. Each has a green test, because each test feeds it the input that same author imagined. The bug only exists in the gap between them — and no unit test that mocks one side will ever see it. The rest of this is how I close that gap and keep it closed. Four ideas. Principle, the real theory under it, then working Python you can lift today. You don’t need my system. Take the concepts.
- One brain: a single source of truth for decisions
- Fail safe, not fail “done”: the default case must be the safe one
- A decision is only as honest as its inputs: never synthesize a fact
- Pick which mistake is cheaper: error asymmetry in a classifier
- The through-line: one brain, honest, and afraid of the right thing
1. One brain: a single source of truth for decisions
// THE PRINCIPLE
Every consequential decision in your system should be computed in exactly one place. Not the data — the judgment. “Is this job done?” “Is this email from a real person?” “Does this case get closed?” Those questions get one function each, and everybody who needs the answer calls that function. Nobody re-derives it with their own little if.
// WHY IT WORKS
You already know the Single Source of Truth idea for data — store a fact once, reference it everywhere, so it can’t disagree with itself. The move almost nobody makes is applying the exact same logic to decisions. A decision is just a derived fact. And the second you compute a derived fact in two places, you’ve created two facts that are only equal by coincidence — a coincidence that has to be re-verified by hand every time either side changes.
Think about what actually happened in Exhibit A. The day Path B started writing richer outcome strings — a note, a timestamp, real-world texture — was the day it silently broke Path A. Nobody touched Path A. It just rotted, because its definition of “done” was a private copy that the rest of the system grew away from. This is the deep reason Don’t Repeat Yourself exists, and it’s almost always taught wrong. DRY isn’t about typing less. It’s about knowledge living in one spot, so a change to that knowledge can’t leave a stale duplicate behind to contradict it. Forked logic is duplicated knowledge with a delayed fuse.
Here’s the mental test I use now. For any important judgment, ask: “if the meaning of this changes next month, how many files do I have to remember to edit?” If the answer is more than one, you don’t have a system. You have several systems that happen to agree today.
// APPLY IT
Take the decision out of the call sites and give it a name. One canonical classifier that both paths route through.
That’s the whole fix and it’s almost boring. The richness of the string stops mattering, because nobody compares the raw string anymore. There’s one definition of “completed,” it lives in one function, and the day it needs to understand a new phrasing, I change it once and every caller gets smarter at the same instant. The test that matters here isn’t a unit test of either path — it’s a test that feeds the real, messy, multi-line strings into the classifier and pins down what they mean.
2. Fail safe, not fail “done”: the default case must be the safe one
// THE PRINCIPLE
Look back at that classifier. It returns "unknown" when it doesn’t recognize the input. That branch is not a loose end. It’s the most important line in the function. When your system meets a value it doesn’t understand, it must do the safe thing — stop and ask a human — never the convenient thing.
// WHY IT WORKS
This is fail-safe design, and it’s borrowed straight from physical engineering, where it’s not optional. A railway signal that loses power drops to red, not green. A dead man’s switch on a train stops the train when the operator lets go. The principle: when the system is in a state it can’t reason about, it collapses toward the outcome that can’t hurt anyone, even at the cost of being annoying. Red-when-confused is a feature.
Software defaults the other way unless you fight it. I had a place where an unrecognized status value fell through to the else — and the else branch quietly auto-closed the case. Think about that. The one situation where the code understood the least was the situation where it took the most irreversible action. A value it had never seen before would sail straight to “done, archived, customer notified.” Backwards. The unknown path is exactly where you have the least right to be confident, so it’s exactly where the action must be smallest.
There’s a security cousin to this worth knowing by name: secure by design — when in doubt, deny. Same instinct. The default answer to a question you can’t resolve is “no,” not “sure.”
// APPLY IT
Make the unknown branch loud and harmless. Never let it be silent and final.
The difference between this and a silent else: pass is the difference between a system that tells on itself and a system that hides its own confusion until a client does the telling. I’ll take the 2 a.m. text every time. A pager that goes off is cheaper than a customer who left.
3. A decision is only as honest as its inputs: never synthesize a fact
// THE PRINCIPLE
If you don’t have a piece of data, the answer is “I don’t have it.” It is never a plausible-looking placeholder you generated to fill the hole. The most seductive version of this mistake is using the wall clock as data: reaching for datetime.now() to stand in for a real-world time you didn’t actually record.
// WHY IT WORKS
I had a loop that documented field events. When the real event time was missing, it filled the gap with “now.” Feels harmless — a timestamp is a timestamp, right? No. A fabricated fact is worse than a missing one, because a missing fact announces itself and a fabricated one lies with a straight face. Mine produced a record that read like a precise, observed moment — “4:02 AM” — that no human ever witnessed. And because “now” is different every time the loop runs, the record changed on every pass, which re-triggered downstream work that keyed off it. A closed job started re-emitting daily, each time stamped with a fresh, invented time. One dishonest input metastasized into a recurring false alarm.
The clean way to think about this comes from functional programming: a pure function, same inputs in, same output out, every time. datetime.now() is the opposite — it’s a hidden input that’s never the same twice. When you splice a value like that into what’s supposed to be a stable record, you’ve made the record non-deterministic, and non-deterministic records can’t be trusted, deduplicated, or replayed. “Unknown” is a stable, honest value. “Now” is an unstable, dishonest one wearing a convincing costume.
// APPLY IT
Let absence be absence. Carry the gap honestly instead of papering over it.
“Time not recorded” on a document looks less polished than a crisp timestamp. It’s also the truth, and the truth doesn’t change between runs, so nothing downstream gets fooled into thinking something new happened. Polish that lies is a liability. Honesty that’s a little ugly is an asset.
4. Pick which mistake is cheaper: error asymmetry in a classifier
// THE PRINCIPLE
Any classifier can be wrong two ways, and the two ways almost never cost the same. Before you tune one, decide which error you can afford and bias the default toward the cheaper mistake. A filter that sorts real from automated messages is the perfect example: dropping a real person’s message is a disaster; letting one piece of junk through is a shrug.
// WHY IT WORKS
Statisticians have a clean frame for this: Type I and Type II errors — a false positive versus a false negative. The trap is treating them as symmetric, two equal columns in a confusion matrix. In the real world they’re wildly lopsided, and the whole art is knowing the exchange rate before you set the threshold.
Mine bit me here. I had soft heuristics flagging “auto-replies” — out-of-office subjects, no-reply-shaped senders — running ahead of the check for “is this a known human I’m in a conversation with.” So a real client replying from an unusual address could trip a soft filter and get silently dropped. I’d optimized to keep junk out (annoying) at the cost of occasionally eating a paying customer’s message (catastrophic). Exactly backwards on the exchange rate. The fix wasn’t a smarter filter — it was reordering the logic so a trusted human bypasses the soft heuristics entirely. Let ten pieces of junk through before you drop one real reply. The errors aren’t equal, so the code shouldn’t treat them as equal.
// APPLY IT
Name the costly error and structure the code so it’s the rare one. Check for “definitely keep” before you ever run “probably drop.”
Same heuristics, different order, opposite risk profile. The structure of the code now encodes which mistake I’m willing to make. That’s the real lesson: your control flow is a statement of your priorities whether you meant it to be or not. Make it say the right thing on purpose.
The through-line: one brain, honest, and afraid of the right thing
Four fixes, one spine. A system should think with one brain — every important judgment computed once, in a place everyone calls, so it can’t quietly disagree with itself. That brain should be honest — it never invents an input to look more finished than it is. And it should be afraid of the right thing — when it’s confused, it stops; when it must guess, it guesses toward the error it can survive.
None of this is exotic. Single Source of Truth, fail-safe defaults, pure functions, error asymmetry — these are old, well-worn ideas, and I learned every one of them by getting burned, not in a classroom. That’s kind of the point. You don’t need the pedigree to use the principles. You need a production system that’s hurt you, and the willingness to go back and ask the boring question: where does this judgment really live, and what does it do when it doesn’t know?
I count instruments for a living in my other life. You don’t get to assume the tray is right. You verify it’s right, once, in one count everyone trusts, and you build the room so a missing one stops the room. Same discipline. Different tray.
FAQ
What is a single source of truth in code?
It’s the rule that every fact — and every decision derived from facts — is defined in exactly one place, and referenced everywhere else. No second copy that can drift. Applied to decisions, it means one canonical function per important judgment, so two parts of your system can’t reach different conclusions about the same thing.
What does fail-safe mean in software?
When the code hits a state it can’t reason about, it takes the safe, reversible action — usually “stop and alert a human” — instead of the convenient one. The unknown branch should do the least, not the most. Like a signal that drops to red when it loses power.
Why is using datetime.now() as a fallback value a bug?
Because it fabricates an observation that never happened and changes on every run. That makes records non-deterministic — they can’t be trusted, deduplicated, or replayed, and they can re-trigger downstream work endlessly. Carry the gap honestly (“not recorded”) instead.
How do I decide a classifier’s default?
Figure out which of the two errors — false positive or false negative — costs more, then bias the default toward the cheaper mistake and guard the costly one first in your control flow. If dropping a real message is catastrophic and passing junk is trivial, check “definitely keep” before you ever run “probably drop.”
How to build an AI agent that audits your own code — adversarial review and fail-closed design, the machine that catches the silent bug.
Hardening a billing guard in Python — a fail-closed fix on a money path, same instinct as section 2.
The Python bug that silently overcharged clients — another decision that lied quietly until a human caught it.
Killing an alert flood: from 50 iMessages an hour to signal only — the other side of section 4’s error trade-off.
// Built and maintained by Jesse Moraga. Reach me: jesse@jessemoraga.com
Leave a comment