I spent the first half of my career being paid to break things. Reverse engineering binaries, writing exploit chains, collecting zero-days in some of the largest codebases on earth. People assume that work is about finding bugs. Partly it is. The part that kept me in it was something else: a specific kind of knowledge I have never found another way to get.
The moment a system breaks in your hands, you know something about it that its authors do not. Not more facts. A different kind of fact. You know what the system does when it stops being the thing they meant to build. Every author knows their intentions. Almost nobody knows their boundaries.
We have no good name for this knowledge, and our whole culture of testing quietly pretends it does not exist. A test suite reads like a list of assertions about correct behavior. What a breaking process produces is a map of incorrect behavior, and the two maps are not approximations of each other. They are drawn from different territories.
Here is the story that convinced me the second map matters more.
The noisy phone line
In the fall of 1988, Barton Miller handed his graduate operating systems class an assignment that sounds like a prank: pipe random characters into UNIX utilities and watch what happens. The idea came from an accident. One of the co-authors was logged on to his workstation from home over a dial-up line, and rain had affected the phone lines, so frequent spurious characters were coming through. The line noise itself was not surprising, they wrote. What surprised them was that the spurious characters were causing programs to crash.
Then he did the thing that turned an anecdote into science. He replaced the phone line with a random generator and made the accident systematic. Two years later the results were published. His team fed random strings to almost 90 standard UNIX utilities across seven versions of the operating system. On every version, between 24 and 33 percent of the utilities crashed or hung. Miller, Fredriksen and So, An Empirical Study of the Reliability of UNIX Utilities, CACM 33(12), 1990
Sit with the number for a second. This was the core of a mature, twenty-year-old system, not some exotic research code: shells, editors, compilers, mail tools. And the causes were embarrassingly ordinary. Pointer and array errors. Return codes nobody checked. Signed characters where the code assumed unsigned. Nothing in any manual page mentions these failure modes, because a manual page describes the system the author meant to ship. The random string found the system that actually shipped.
The reaction to the paper was to build an industry. Fuzzing went from a course prank to standard practice. OSS-Fuzz, the continuous fuzzing service for open source software, reports over 10,000 fixed vulnerabilities and 36,000 bugs across 1,000 projects. OSS-Fuzz documentation But thirty-plus years later, whether noise works is settled. Why it works is not, and the answer turns out to matter more than the number did. A random string contains no information about the program. It carries no model, no intent, no theory of the code. Why does it keep finding bugs that careful readers miss?
Two kinds of knowledge
Every system carries two sets of facts about itself.
One set lives at design time. The invariants, the types, the documented behavior, the mental model the author had while writing. This knowledge is articulate. It can be taught, documented, transmitted in a code review. It is also, in a precise sense, circular: the author writes code to satisfy the model, then checks the code against the model. Both sides of the loop are made of the same intentions.
The second set lives in the failure space. What the system does at the boundary cases, under inputs no one imagined, on the paths that were never walked. This knowledge is mute. No document contains it, because it is defined negatively: it is everything the design does not cover. The only way to learn it is to probe. You cannot read your way to a system’s boundaries. You have to go there.
I think this is the actual content of the skill people call “experience”. An experienced engineer is someone whose map of the failure space is better populated, not someone who knows more facts about systems, because they have spent years walking into regions that were never on anyone’s diagram.
And here is the part that took me years to see clearly. Every test you write yourself samples from the region you already imagined. Your test cases are drawn from the ball of inputs you designed for, because your imagination lives there. A test suite is a survey of your own intentions. That is why a 100% passing suite can coexist with a 30% crash rate under random input. Miller’s utilities passed whatever tests their authors ran. The random strings sampled a different territory entirely.
Why random input works
The mechanism is combinatorial, and it is worth spelling out because it explains both the power and the limits of the technique.
The input space of any real program is astronomically larger than the set of inputs its designers considered. A program that accepts a 100-byte string accepts 256^100 possible inputs; the test suite covers, generously, thousands. The ratio between imagined and actual is not a small gap. It is the difference between a room and a universe.
Now consider what a random string does: it samples from the universe. Almost every sample lands outside the ball of imagined inputs, in regions where the program’s behavior was never specified because nobody was thinking about that region when they wrote the code. Bugs cluster there, because bugs are precisely the behavior that lives outside the specification. A random string is a cheap ticket to the one place your knowledge doesn’t reach.
This also tells you exactly when noise will fail. If the bugs you care about require understanding the program’s semantics, its logic, its intent, then random sampling finds nothing: the bug lives at a semantic depth that random walks do not reach.
The pattern
Once you see the shape, it shows up everywhere. Four results from four different fields, one identical structure.
The first is Miller, 1990: pure noise crashes 24 to 33 percent of mature UNIX utilities.
The second is adversarial machine learning. In 2013, researchers showed that tiny, almost invisible perturbations to an image flip a network’s answer completely. Szegedy et al., Intriguing properties of neural networks, 2013 A year later, others pushed further: images that are completely unrecognizable to humans, pure noise patterns, that a state-of-the-art network classifies with up to 99.99 percent confidence. Nguyen, Yosinski and Clune, 2014 The network’s training had never visited those regions of input space, and its behavior there is something else entirely, not a smaller version of its behavior here.
The third result shows what happens when defenses are built without facing this honestly. At ICLR 2018, nine papers proposed defenses against adversarial examples. When researchers later examined them, seven of the nine relied on obfuscated gradients, a defense pattern that works by making the attacker’s optimization fail rather than by making the model actually robust. Six were circumvented completely, one partially. Athalye, Carlini and Wagner, 2018 Each defense passed every test its authors constructed, because each test was built from the author’s own model of what an attack looks like. The perturbations that broke them came from outside that model.
The fourth is happening now. In late 2024, Google’s Big Sleep agent, an LLM-based system built by Project Zero and DeepMind, found a previously unknown, exploitable stack buffer underflow in SQLite, reported it, and watched it get fixed the same day, before the bug ever shipped in a release. Project Zero, From Naptime to Big Sleep, 2024 Eight months later the same program was credited with flagging a vulnerability that threat actors were preparing to exploit, before the exploitation happened. Google Cloud, 2025 The agent is not generating random strings, and what it is doing instead is the hinge of the last section.
Four results from four corners of the discipline: software reliability, machine learning, security, AI agents. One shape every time: a system built inside a region of possibilities, tested against that region, broken from outside it.
The diagram above is schematic, but the trap it shows is real. The accuracy-versus-perturbation curves in the robustness literature all look like this in outline: flat, flat, flat, cliff. The flat part teaches you nothing. It just says the region you are in is the region you were built for. The only informative point on the curve is the cliff edge, and the cliff edge is always outside the range you tested. So run the sweep until it breaks. Test to failure, not to threshold. A passing test inside the plateau is a measurement of your own caution.
From noise to knowledge
A crash is a lead, not knowledge.
Miller’s 1990 paper produced a list of bug reports and fixes, but each crash arrived wrapped in megabytes of random garbage. Turning noise into understanding took another piece of the puzzle. In 2002, Andreas Zeller formalized it: delta debugging, an algorithm that takes a failing input and automatically shrinks it to the minimal input that still triggers the failure. Zeller, Simplifying and isolating failure-inducing input, IEEE TSE 28(2), 2002
This is the step that converts noise into a sentence a human can read. The random string says: something here breaks. The minimized case says: these three bytes, in this order, with this state, break it. The first is a signal. The second is an explanation. The pipeline is noise, crash, minimize, understand, and most production fuzzing systems are some version of it.
Notice the division of labor. The noise contributes the thing cleverness cannot: contact with regions outside the imagined ball. The minimization contributes the thing noise cannot: a compression of the accident into a cause. Both halves are necessary. Either alone is nearly useless.
Where it stops
Now the limits, because they are as instructive as the power. Noise is a tool with a domain, not a philosophy of everything.
Random input finds shallow bugs. Look again at Miller’s crash causes: pointer errors, unchecked return codes, signedness. All real, all worth fixing, all shallow. A random walk stumbles into the failures that sit near the surface of input space. Deep logic bugs, the ones where the code does exactly what it says and what it says is wrong, are vanishingly rare in random samples. Noise bounds your ignorance about the surface. It says nothing about the depths.
Random input knows nothing about adversaries. A real attacker’s input is the opposite of structureless: it is maximally structured, optimized against your specific system. Fuzzing with random strings measures your risk against nature, against accident and chaos. It does not measure your risk against an opponent, which is a different and smaller and more dangerous territory. Even the certified defenses from randomized smoothing, which train and evaluate under explicit noise, certify robustness against a bounded adversary, not an unbounded one. Cohen, Rosenfeld and Kolter, 2019
And volume is not insight. A fuzzing deployment produces thousands of crashes a month; triage, deciding which ones matter, becomes the actual bottleneck, which is why OSS-Fuzz’s architecture is mostly machinery for deduplicating and ranking crashes rather than generating them. The noise is the cheap part. The understanding is the expensive part, and no amount of noise substitutes for it.
Which brings back Big Sleep, and why it is a different kind of interesting. The agent that found the SQLite bug was not sampling randomly. It was reading code, forming hypotheses about which edge cases the authors had not considered, and constructing inputs to test those hypotheses. That is structure: semantic structure, the thing random noise cannot have. But notice what made it work. The agent’s value came from combining semantic reasoning with the breaker’s move: actively probing for the behavior outside the specification, then minimizing and explaining what it found. The noise-only era found the surface. The semantic era, if it works, starts on the depths. The pipeline is the same. The hypothesis generator changed.
What I actually believe now
I used to think understanding a system meant being able to say how it works. I no longer believe that. A complete explanation of the happy path is available to anyone who reads the documentation. The knowledge that matters lives where the documentation has no opinion, and the only way in is to be wrong there on purpose, with a method.
So the practical version, for the systems I build now: put the noise generator inside your CI, not inside your adversary’s toolchain. Run every sweep to failure, not to threshold, and treat the flat part of the curve as uninformative by definition. When something breaks, minimize before you read. And when nothing breaks, do not celebrate. A quiet sweep means the noise has not reached far enough yet. The system you have not broken is not a working system. It is an unasked question.