Imagine two companies, Company A and Company B. They have the same budget and the same deadline. They have teams comparable in size and skill. They are building on the same tech stack. The only difference: A builds in secret and will ship only on the deadline; B builds in the open, ships to testers, and iterates on what it learns.
If you had to bet on which will deliver the better product, which would you choose?
I would pick B. I think B works in a way that increases the opportunities for learning, and that will lead to a better product.
Software development is at its core a process of knowledge creation. Knowledge of the problem, the domain, the users. Code matters, it matters a lot, but only as the way to materialize the knowledge into a usable solution.
If you want to build better software, better products, it’s therefore useful to learn about how knowledge is created.
The 20th-century Austrian philosopher Karl Popper gave a compelling explanation of how knowledge grows: through conjecture and refutation.
Popper’s main claim is that all knowledge is conjectural. There is no authority or solid ground on which to justify knowledge. All we have are conjectures, explanations, guesses. But that is no cause for despair. We don’t need justifications to make progress, we just need to correct the errors in our guesses. The engine of the whole process is criticism.
People notice problems in the world and guess at solutions. That’s the conjecture step. The proposed solutions are then scrutinized through severe, strict, and honest criticism. That’s the refutation step. If a solution survives, it is adopted tentatively.
This process, Popper found, never ends. A new solution introduces new problems or reveals problems that were hidden before. That’s how progress happens.1
If software development is knowledge creation, and knowledge grows through criticism, then the more we criticize the theories behind the choices in our software and their implementation in code, the more chances we have to discover and correct errors.
This is why techniques like test-driven development, continuous integration, continuous delivery, and feature flags lead to better products. They are all ways to criticize, to identify errors, in our software. TDD and CI accelerate error detection in the developer’s understanding of how the code runs. Is the code doing what I expected it to? CD and feature flags accelerate error detection in the developer’s understanding of how users interact with the product. Is the code doing something worth doing?
Both Company A and Company B could be working using TDD, CI, and other practices for fast feedback on whether the code is correct. But only Company B exposes its early ideas to real world criticism, giving itself many chances to correct its errors. That’s the difference that makes Company B the best bet.
Notice, however, that there is no guarantee of success. Critical errors may remain hidden from Company B, or they might fail to create solutions for the ones they know about.
Popper offers no certainties. In fact, he shows how nothing is certain. What he does is shift the focus from “I have to get it right” to “I have to find and fix errors.”
Let’s go discover some errors.
A couple of paragraphs in a short post cannot do justice to the depth of Popper’s work. If you’re curious, you could start with the essay Science as Falsification, then the lecture collections In Search of a Better World and All Life is Problem Solving. Bryan Magee’s short book Popper is also a good way to get started.


