Engineering Philosophy · Part 4 of 4
Alive to Guess Again
Karl Popper argued that a theory which can't be proven wrong isn't really saying anything. The same is true of engineering practices: if you aren't actively trying to break them, you don't know whether they're working.
David Shortland 24 March 2026 8 min read
There's a problem with justification that I haven't addressed. You can justify almost anything if you're allowed to be vague enough. We do standups because they improve communication. We write tests because they improve quality. We use microservices because they improve scalability. These sound like reasons and they have the shape of reasons, and something important is missing from all of them.
Nobody is trying to prove them wrong.
Popper's Razor
Karl Popper was a philosopher of science who spent most of his career on a single question: what separates real science from things that merely look like science? His answer was falsifiability, and the idea goes deeper than most people realise when they first meet it.
Popper wasn't only saying that theories should be testable. He was saying that science progresses by actively trying to destroy its own theories. You accept a theory provisionally, as the best available explanation, and then you do everything you can to break it. You don't test it in the easy cases; you test it at the extremes, in the conditions where it's most likely to fail. If it survives serious attempts at refutation it earns its place for now. The moment it does fail, you discard it and move on.
The distinction matters. Proving gravity by dropping a ball is trivial, because everyone already knows the ball will fall. The real test is at the boundaries, near a black hole or at quantum scales, in the conditions where the theory might break down. Easy confirmations tell you nothing, and hard tests are where knowledge lives.
Popper's classic examples were astrology and certain readings of Freudian psychoanalysis. An astrologer can explain any outcome after the fact, and if a prediction was wrong there's always a reason: another planet was in retrograde, the birth time was imprecise, the subject wasn't receptive. The theory never fails because it can absorb any result. Einstein's general relativity made a specific, testable prediction about how light bends around massive objects, and if the 1919 eclipse observations had shown no bending the theory would have been wrong. That vulnerability is what made it valuable.
Or as Popper put it: good tests kill flawed theories; we remain alive to guess again.
I came to Popper through a mentor's recommendation, and the moment I understood the argument I started seeing unfalsifiable claims everywhere in software engineering. Worse, I started seeing them in my own work.
The Unit Test Problem
Here's something I did that taught me this concretely.
I was working on a system and decided it needed better test coverage. That felt like an obviously good decision. Tests improve quality, everyone knows this, so I went through the existing codebase and wrote unit tests for the code that was already there.
The tests passed, coverage went up, and it felt productive. What I was doing was the equivalent of dropping a ball and confirming that gravity works. Every test I wrote verified that the code did what the code already did. I'd look at an implementation, understand its behaviour, then write an assertion confirming it. Easy confirmations, all of them, testing the theory that the code is correct in the most comfortable conditions available: the normal inputs, the happy path, the cases I already knew worked.
What I never did was try to break it. I never asked what the boundary conditions were where this logic might fall apart, what inputs would expose a flaw in my assumptions, what the black hole for this function looked like. I was accumulating confirmations instead of attempting refutations.
The coverage number looked good and the test suite was unfalsifiable in practice. It couldn't fail in a way that told me anything I didn't already know. If a test broke it was because someone had changed the implementation, never because it had caught a genuine behavioural problem. The tests were a mirror held up to the code, reflecting it back at itself.
What I should have done is what TDD actually intends. Define the expected behaviour first, write code to satisfy it, and include the edge cases and boundary conditions where the behaviour might break. A test that says "when a driver completes a session, their lap times are ranked and the fastest is marked" is testing a business rule at its core. The Popperian step is the next one. What happens when two lap times are identical? When the session has zero laps? When it has exactly one? Those are the hard tests, and they're the ones that kill flawed implementations.
In a small team you can't afford to write tests for the sake of coverage. Every test should encode a business rule that would cause a real problem if it were violated, and the most valuable ones test that rule in the conditions where it's most likely to break.
The Pattern Is Everywhere
Once I started looking for practices that had never survived a serious attempt at refutation, I couldn't stop finding them.
Standups. Most teams justify standups as improving communication or keeping everyone aligned, and most have never tried to falsify the claim. Defining what success looks like and then passively waiting to see whether it happens isn't enough. The Popperian approach is to go looking for the failure. Ask the team whether anyone had a coordination problem this week that the standup should have caught and didn't. Whether anyone sat through it already knowing everything that was said. Whether anyone withheld a problem because the format didn't make it safe to raise.
If you go looking for failure and can't find it, the practice has survived a genuine test. If you find failure immediately, you've learned something valuable. Either way you know more than you did, and most teams never ask. The standup continues, provisionally accepted and never tested at the extremes, until it becomes a ritual that can't fail because nobody is trying to make it fail.
Code reviews. The justification is usually catching bugs or sharing knowledge. Track what actually happens in your reviews and you might find that 90% of the comments are about formatting, naming or style, and almost none of them catch logic errors. "Do reviews happen?" is the easy test. The hard one is whether a code review has ever caught a bug that would have reached production, how often, and what kind. If you go looking for that evidence and can't find it, the practice has been falsified. It isn't doing what you claimed it does. It might well be doing something else that's valuable, and the original justification is dead, so you should either update it or drop the practice.
Retrospectives. Teams run retrospectives to continuously improve. A serious attempt at refutation would be to pull up the action items from the last three retrospectives and count how many were completed and how many led to a measurable change in how the team works. If the answer is that you don't track that, the practice has been insulated from failure. You've been dropping the ball and confirming that it falls.
Provisional Acceptance
There's a subtlety in Popper's thinking that changes how I approach all of this. He never said that unfalsified theories are true. He said they're provisionally accepted: they've survived testing so far, they're the best explanation available, and new evidence could overturn them tomorrow. The provisionality is the whole point. Treat a practice as permanently justified and you stop testing it.
That's the difference between "we do standups because they work" and "we do standups because they've survived our attempts to find evidence that they don't, and we'll keep looking." The first is a settled belief that can't be wrong. The second is a living hypothesis. Being wrong is how you learn.
The same principle applies to architectural decisions, technology choices, team structures and deployment processes. Hold them provisionally. Test them where they're weakest, and drop them when the evidence turns against them.
Why This Is Hard
Unfalsifiable practices survive because actively trying to break your own processes is uncomfortable. Define what failure looks like, go looking for it, and you might find it. Then comes the admitting, the changing course, and possibly some difficult conversations. Keeping the justification vague and the testing gentle is much easier.
Popper noticed the same dynamic in science. Unfalsifiable theories are popular because they're safe: they explain everything, predict nothing, and never require their proponents to change their minds. Falsifiable theories are dangerous. They put themselves on the line, and the danger is what makes them useful.
The connection to the pragmatist's razor is direct. That post argued that every deviation from a principle needs a specific justification. This one adds the extremes. Test a justification where it might fail, and when it fails, let it go. Good tests kill flawed practices. We remain alive to guess again.
I approach testing differently now. Before writing a test I ask what business rule it encodes and what inputs would break it. Not the happy path. The edge cases, the boundary conditions, the black holes. Coverage as a metric has become almost irrelevant to me, and what matters is whether each test represents a genuine attempt to falsify the assumption that the code is correct.
More broadly, I've started treating every practice as a provisional hypothesis. Standups, reviews, architectural patterns: they're all theories about what works, and most of them have only ever been confirmed gently.
I don't always get this right. The pull toward easy confirmation is strong and it takes discipline to go looking for evidence that you're wrong. That discipline is what Popper was arguing for. Testability as a habit of mind, and the honesty to update a belief when it breaks.