Tests Aren’t Medicine

A while back I got into an argument on Twitter (back when it was still called that). Somebody posted that testing is the medicine that makes your application better. I replied that testing is the diagnostics that tell you your application needs medicine. They came back and said no, testing is the medicine, because it fixes the problems you have. Then a few more people chimed in with “testing is both,” which somehow annoyed me more than the original post.

So let’s take a look at what testing actually does for you and what it doesn’t, because I think the confusion does real damage. Don’t misunderstand me, I’m not saying testing isn’t important. It’s critical, and if you’re not doing enough of it you should go do more. I’m saying that a test, by itself, doesn’t fix anything. It tells you that you have a problem, and that’s all it does.

The light on the dash

Everybody reaches for the doctor analogy here, but I think your car works better, because a car has a whole range of testing built into it.

Low tire pressure is a real problem. The car handles worse, takes longer to stop, and you’re a lot more likely to end up with a blowout. The US government took it seriously enough to pass a law about it. In August 2000 Firestone recalled 6.5 million tires, mostly ones fitted to Ford Explorers, because the treads were peeling off, and a big part of the fight in the congressional hearings that followed was over tire pressure (Ford told Explorer owners 26 psi, Firestone said 30). Congress passed the TREAD Act that November, and NHTSA eventually required a tire pressure monitoring system on every new light vehicle starting in September 2007.

So think about how tire pressure gets tested over the life of a car. When you bought it, the dealer probably went around with a gauge and made sure all four tires were right. That’s a one-time test, and it’s not nearly enough, because tires leak.

You could do better by checking the pressure every time you stop for gas, which is the equivalent of running your tests on every build. I don’t do that. You know I don’t do that, so I’m not even going to pretend I do. My dad, maybe, but not me. It’s a good idea and you should do it.

And if your car is newer than about 2007, there’s a sensor watching the pressure all the time, with a light on the dash that comes on when it’s low. How much that light tells you depends on the car. Some of them show you the actual pressure in every tire, and some are so simple they don’t even say which tire is low, so you get to walk around the car and find out for yourself (and it’s always the last one you check). So we’ve gone from one-time testing to scheduled testing to continuous testing, which is pretty much the path software has taken over the last twenty years, and everybody feels very modern about it.

Now you’re driving down the road, the car feels a little squirrely, maybe you notice and maybe you don’t, and the light comes on. Low tire pressure. Great, the test worked. If tests are medicine, you’re done, because the light came on and the problem is handled, so keep driving.

Nope, it doesn’t work that way. You have to pull over and put air in the tire, or put on the spare, or get it to someone who can patch it. The sensor finished its entire job the moment the light came on, and the tire is exactly as low as it was a second earlier.

That’s the point. Tests detect problems and let you know they’re there, and they don’t do a single thing about them. Somebody still has to.

(And yes, I know people who have driven around for months with that light on. That’s a different problem, and it deserves an article of its own.)

A green light tells you less than you think

The sensor also only knows what it was built to know. The federal standard says the light has to come on when a tire is 25 percent or more below the recommended pressure. So when the light is off, you don’t actually know your tires are fine. You know none of them is a quarter low. And the simple system that just says “a tire” gives you a lot less to work with than the one showing all four pressures, even though both meet the same federal rule. The tire sensor also won’t say a word about your oil, because that’s a different sensor. Every test tells you something different, and only the thing it was built to tell you.

Dijkstra said it better than anybody, back around 1970: “Program testing can be used to show the presence of bugs, but never to show their absence!” When your suite runs green, you don’t know the application is good. You know it isn’t bad in the particular ways you thought to check.

“But my tests do make the code better”

To be fair to the other side, there are smarter versions of the “tests are medicine” argument than the one I ran into on Twitter, and they deserve an answer.

If you do test-driven development, you’ll tell me that writing the test first changes the code, because having to make something testable pushes you toward a better design. I agree that happens. But look at what’s doing the work. It’s the thinking you had to do about what the code is supposed to do and how you’d know if it did, and the test is where you wrote that thinking down. The design got better because you thought harder, and the test is the receipt.

You’ll also say that regression tests keep old bugs from coming back, and that sure sounds preventive. It’s still a detector, though, just one pointed at last year’s mistake. If somebody reintroduces that bug, the regression test doesn’t stop them from writing it. It turns on the light, and somebody still has to go fix it.

Then there’s the newest version. Hook an AI agent up to your test suite, and when a test fails the agent patches the code and reruns until everything’s green. Test and fix in one loop, so isn’t that medicine? I’d say it proves my point. Something other than the test did the fixing, and the agent only knows what “fixed” means because the test told it. If the test checks the wrong thing, the agent will keep changing your code until the wrong thing passes. It’s like the mechanic who keeps swapping parts until the check engine light goes off (in the trade they call that firing the parts cannon). Eventually the light does go off, and you’re left with a big bill, a car full of new parts, and no idea whether the thing that was actually wrong ever got fixed.

Where this actually hurts

Why does any of this matter beyond winning an argument on the internet? Because the medicine mentality is how people end up believing that if they just do enough testing, their software will be safe and secure and reliable. And it won’t be.

Security is where I see this the worst. Teams treat pen testing and DAST and fuzzing as if they take the place of building secure code. They don’t take the place of anything. They tell you the software has problems, which is valuable and necessary, and then somebody has to fix those problems, and ideally figure out why they keep showing up.

Go find the nail

Manufacturing figured this out a long time ago. Harold Dodge, who helped invent statistical quality control at Bell Labs, put it as “You can not inspect quality into a product.” Deming spent decades hammering the same idea into American industry: “Inspection is too late. The quality, good or bad, is already in the product.” Swap “test” for “inspect” and you’ve got this whole article, about forty years late.

So what do you do when the light comes on? Three things, in order.

  1. Fix what it found. In the code, not in the test (unless the light wasn’t supposed to come on in the first place, but again, that’s a different article). Adjusting the test until it goes quiet is the software version of putting a piece of tape over the dash light. (I hope you aren’t doing this.)
  2. Ask why it happened. If the same tire keeps going low, at some point you stop topping it up and go find the nail.
  3. Change how you build so that kind of problem is hard to create in the first place. Coding standards, static analysis, memory-safe languages, design reviews, whatever closes off that class of mistake before it ever reaches a test.

Now, I’m not saying prevention is free. Tire makers have been working on airless tires for years (Michelin’s prototype is called Uptis), and you can see the appeal, because a tire with no air in it can’t go flat. But they’re harder to make, they’re going to cost more, and from what’s been reported so far they ride stiffer, pass along more road noise, and have a harder time getting rid of heat at highway speeds. So you weigh the chance of a flat against all of that, and for most of us today the answer is still a regular tire and a pressure sensor. Software works the same way. Every one of those prevention techniques costs something, whether it’s tooling, training, slower builds, or rewriting code in a new language, so look at the risk, the benefit and the cost, and spend where it pays off. For a pacemaker or a flight controller that math comes out very differently than it does for your marketing site.

Memory-safe languages are the airless tire of software, and the tradeoffs look a lot alike. The upside is huge. The Chromium team says around 70% of their high-severity security bugs are memory safety problems, and Microsoft has reported about the same for the vulnerabilities in its own products. That won’t be true of everybody’s code, but it shows you how much is on the table. In a language like Rust the compiler stops most of that class of mistake at build time, before there’s anything to test. That’s prevention in its purest form. But it isn’t free either. Somebody has to rewrite or wrap a lot of existing C and C++, your team has to learn a new language, the tooling and certification story for safety-critical work is still catching up, and the moment you call into old C code (or use the unsafe keyword) some of the guarantee goes out the window. And it does nothing at all for logic bugs, so you still need the sensors. Whether it’s worth it for your code is exactly the risk, benefit and cost question, and that’s a story for another day.

And then keep testing, because you still need to know when something gets through. Keep the sensors, and keep checking your tires at the gas station if you’re more virtuous than I am. Just don’t confuse the light on the dash with air in the tire.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.