Two services with the same bug share an ancestor; two with the same right answer tell you nothing. Your codebase is a manuscript tradition — learn to read its errors like one.
Two services in your company start lying about time.
The billing service, written in Go by one team, and the notifications service, written in Python by another, begin stamping Eastern-time events exactly one hour early. Not always — only between the second Sunday in March and the first Sunday in November. The rest of the year they're perfect. The two services share no code anyone knows about. Different languages, different repos, different on-call rotations, different floors. The obvious move is to file two tickets, assign them to two teams, and write the coincidence off as one of those things.
Don't. That shared, specific, oddly-shaped failure is not a coincidence. It is a fingerprint, and it is telling you something the architecture diagram is not: these two services have a common ancestor. Both of them, somewhere up their lineage, inherited the same code that hardcodes a −5 offset for Eastern time and never learned about daylight saving. The bug is a signature, and the signature proves descent.
The discipline that knows how to read that signature with rigor isn't software engineering. It's a roughly two-hundred-year-old method for reconstructing the lost originals of ancient books — and it worked out the exact logic you need, in precise technical vocabulary, before electricity was a consumer product.
Almost no ancient text survives in the author's own hand. What survives is a tradition: copies of copies of copies, each scribe introducing fresh errors, the autograph long since dust. “The” Iliad, “the” New Testament, Lucretius's De rerum natura — each reaches us as a branching family of hand-copied manuscripts that disagree with one another in thousands of places. The scholar's problem is brutal: given this scatter of imperfect witnesses, which ones are related, and what did the lost original actually say?
The name attached to the answer is Karl Lachmann (1793–1851), though he neither invented the idea nor wrote it up as a theory — he simply applied it to Lucretius in 1850 so persuasively that a generation of classical philologists adopted it. (The roots run back further, through Bentley and Bengel in the 1700s.) Almost a century later, Paul Maas compressed the method into an austere little book, Textkritik (1927), that remains its canonical statement.
The founding principle fits in five words:
Community of error implies community of origin.
If two manuscripts share a distinctive error — not a variant, not a coincidence, not something either scribe could have fixed on his own — they almost certainly descend from a common ancestor that already carried that error.
And here is the half of the principle that engineers consistently miss, the half that feels wrong the first time you hear it: shared correct readings prove nothing. Every competent scribe copies most of the text correctly. Correctness is the boring background hum of the entire tradition; it carries no information about who is related to whom. The genealogical signal lives entirely in the mistakes — and not even all of them. Only the specific, distinctive, non-obvious ones.
Sit with how exactly that describes your systems. Every competent service returns the right answer almost all the time. Two services both getting the right answer tells you precisely nothing about whether they share a single line of code. It's the errors — the weird, embarrassing, specific errors — that draw the real map.
Maas didn't just wave at “shared mistakes.” He built a calculus, and its two load-bearing terms map straight onto debugging.
A conjunctive error (Maas's Bindefehler) is a distinctive mistake that links two witnesses. The textbook case is the eye-skip: a scribe copying a passage where two nearby lines end with the same words lets his eye jump from the first ending to the second, and silently drops everything in between. The classicists have a name for it — homoeoteleuton — and its consequence is exactly what you'd expect: every manuscript later copied from that damaged copy is missing the very same passage. Find two manuscripts with the identical gap, and you've found two members of one family.
The software version needs no translation. The same off-by-one on the same obscure boundary. The same mishandled edge case. The same hardcoded −5. When two services share a Bindefehler like that, they are not two bugs. They are one bug, copied — a conjunctive error that groups your services into a lineage.
A separative error (Maas's Trennfehler) is the opposite tool, and it's the one engineers never think to use. It's an error present in one witness but absent in another, where the clean witness could not plausibly have just guessed its way to the correct reading. A separative error proves two witnesses are not descended from the same intermediate — it cuts a branch rather than joining one. In code: Service A has the daylight-saving bug; Service B handles Eastern time correctly, in a robust way it could not have stumbled into by luck. That correct handling is a Trennfehler. It tells you B does not share A's timezone ancestor, however similar they look elsewhere. You use conjunctive errors to build the family tree and separative errors to prune it — to rule suspects out, not just in.
This is the part worth internalizing: the discipline is not the crude rule “same bug means related.” It's a careful judgment about which errors are diagnostic (Maas called the diagnostic ones Leitfehler, guide-errors) and which are just noise that any copyist might produce on any day.
Not every shared bug proves anything, and the criterion is the whole game. A diagnostic error has to be two things at once: distinctive (unlikely to arise independently) and non-obvious (not the kind of thing a competent copyist would either fix on sight or fall into on their own).
A missing null check on a common pattern fails the test. So many developers write that bug, independently, every day, that finding it in two services tells you nothing — it's the coding equivalent of two scribes both modernizing an archaic spelling. They didn't coordinate; the mistake is just in the water. Textual critics learned to discard exactly this class of variant, because a thousand scribes “correct” the same hard spelling without ever having met.
But the same wrong-but-plausible word in the same place — or the same hardcoded −5, the same fencepost error on the same weird boundary — is a fingerprint, because independent production is wildly unlikely. The test, stated crisply enough to use in a code review, is this:
Would two developers, working entirely separately, plausibly produce this exact mistake?
If yes, it's a trivial variant; it proves nothing; move on. If no, you are looking at a conjunctive error, and an ancestor exists. And the corollary is the useful one: the weirder the bug, the stronger the evidence. A bug so strange you can barely believe one team produced it is the strongest proof imaginable that they didn't produce it twice.
There is one class of conjunctive error so decisive it deserves its own paragraph, and it's the cheapest forensic tool you own.
If two “unrelated” services both log Conneciton refused — same word, same transposition of the same two letters — you are not looking at a coincidence. You are looking at copied code. Scribal spelling slips are textual critics' cleanest Bindefehler precisely because nobody re-derives them; a shared typo in an error string, a log message, or a code comment is the cleanest in software for the same reason. (Is it literally impossible for two people to fumble the same keys the same way? No. But as evidence it's about as close to a smoking gun as debugging ever hands you.) The practical consequence is almost unfair: grep your entire organization for that one misspelled string, and you will frequently surface the common ancestor — the vendored module, the copied helper, the snippet that made the rounds — in a single query. The typo you'd be embarrassed to ship is the best lineage tracer you'll ever have.
Lay the two fields side by side and the punchline lands on its own. A textual critic assembles the witnesses into a stemma codicum — a family tree with an archetype at the root (the latest common ancestor of everything that survives), hyparchetypes as the internal nodes grouping sub-families, and the surviving manuscripts as leaves. They reconstruct that tree from shared errors, then reason up it from the leaves.
That is a dependency graph. Scholars have been doing dependency-graph reconstruction from shared defects for nearly two centuries; we just call our version npm ls and the software bill of materials. The methodology is identical. Only the vocabulary differs.
So when a conjunctive error surfaces, run the philologist's procedure:
Honesty requires the limit, and the philologists hit it first. Maas's clean trees assumed each manuscript had a single parent. Giorgio Pasquali, in his Storia della tradizione e critica del testo (1934), pointed out that real manuscripts are contaminated: a scribe copies from two exemplars at once, so a witness has two parents and the stemma becomes a network rather than a tree. Maas had treated contamination as catastrophic for the method. Pasquali showed it was the ordinary case.
It's the ordinary case for you, too, and the analogy is exact. A service vendors a helper from one internal repo and pulls a library from a public registry, and both happen to carry a similar defect. Or — subtler — two teams independently read the same confusing API spec the same wrong way: here the shared “ancestor” isn't any code at all, it's the specification, and the bug is a convergent misreading rather than a copied one. When the signals conflict — Service A's failure matches both library L and copied module M — you do not have a clean tree, and you must not force one. The mature move (the neo-Lachmannian one) is to treat the stemma as one argument among several, weight the most diagnostic error most heavily rather than just counting shared ones, and stay honest about ambiguity. And note the special case: if both services hit the bug through the same standard-library version, the common ancestor is the language runtime itself — the archetype is the platform, and the fix lives upstream of you entirely.
Here is the reframe, and it is genuinely hard for engineers to accept, because it inverts the instinct that working software is what you should study: your successes are architecturally uninformative. Two services returning the correct answer on the same input tell you nothing about whether they share a line of code, for the same reason two manuscripts with the same correct sentence tell a philologist nothing — every witness gets the easy readings right. Correctness is the background. It is your failures, and specifically your weird, distinctive, slightly embarrassing failures, that reveal how your systems are actually related, which is almost never how the org chart says they are. The diagram swears these two services are unrelated. The shared bug says they have a great-grandparent in common. Believe the bug.
So the next time you find the same strange failure in two places that “can't possibly be related,” don't open two tickets. Open one investigation. Ask the question a textual critic has been asking since 1850: could these two have made this exact mistake independently? If the answer is no, stop debugging the symptoms in parallel and go find the ancestor — because there is one, and it is still out there with children you haven't met.
Shared correctness is uninformative. Shared distinctive error is a lineage map. Scholars used that map to rebuild Lucretius from a scatter of error-ridden copies nearly two centuries ago. Your codebase is a manuscript tradition, copied by hands that were tired and in a hurry. Learn to read its errors like one.
Stemmatics reconstructs lineage after the outage. You can record it before.
A textual critic rebuilds the family tree from the errors copies left behind — because the original provenance was never written down. You don't have that constraint. When an agent vendors a helper, calls a tool, or copies a decision from somewhere upstream, that ancestry can be recorded as it happens instead of reconstructed from shared bugs at 3 a.m. Chain of Consciousness anchors every agent action to a verifiable provenance chain — so the “common ancestor” isn't something you grep for after the second service pages you; it's already in the record.
See a verified provenance chain · Hosted Chain of Consciousness
pip install chain-of-consciousness · npm install chain-of-consciousness