LabHub

Blog

Epistemology in Software Engineering: Knowledge and Certainty

한국어English日本語

Epistemology and software engineering

Introduction: Do We Really "Know"?

Software engineers work every day on top of judgments that begin with "I know." I know what this code does. I know how this system behaves. I know why this architecture is right. But is that "knowing" really knowledge?

Twenty-five hundred years ago, Socrates said, "I know that I know nothing." That humble confession became the starting point of epistemology in Western philosophy. Epistemology is the branch of philosophy that investigates the nature, scope, and limits of knowledge, asking fundamental questions: "What can be known?", "What is knowledge?", "Are our beliefs justified?"

Remarkably, these ancient questions map precisely onto the central problems of modern software engineering.

"Program testing can be used to show the presence of bugs, but never to show their absence." - Edsger Dijkstra, "On the Cruelty of Really Teaching Computing Science" (1988)

Dijkstra's famous warning is, at bottom, an epistemological claim: our knowledge of software correctness is fundamentally incomplete.

This article applies the major concepts of epistemology to software engineering, analyzing the traps of technical certainty and exploring why a culture that admits "I don't know" produces better software.


Chapter 1: Core Concepts of Epistemology and Software Engineering

What Is Epistemology?

Epistemology is a compound of the Greek words episteme (knowledge) and logos (study) -- the study of knowledge. Plato defined knowledge as "justified true belief." Under this definition, saying that you "know" something requires three conditions to hold.

  1. Belief: the subject must believe the proposition
  2. Truth: the proposition must actually be true
  3. Justification: the belief must be supported by rational grounds

Substituting software engineering into these three conditions yields an interesting insight.

What "Knowing" Means in Software Engineering

Epistemic conditionSoftware engineering contextCommon illusion
Belief"This code behaves correctly"Certainty from having read and understood the code
TruthBehaves correctly on every input and every stateBehavior confirmed only on the cases that were tested
JustificationSufficient testing, proof, verificationTreating a passing subset of tests as proof of total correctness

In Thinking, Fast and Slow (2011), Daniel Kahneman divided human thought into System 1 (fast, intuitive) and System 2 (slow, analytical). When engineers judge that "this code is right," System 1 is usually the one doing the work: skimming the code, recognizing a pattern, and arriving intuitively at "this is correct." But that intuitive judgment is vulnerable to a long list of cognitive biases.


Chapter 2: Three Epistemic Illusions

Illusion 1: I Read the Code = I Understood It (The Readability Illusion)

The most common trap in code review is the illusion that "I read it, therefore I understood it." Gerald Weinberg identified this problem as early as The Psychology of Computer Programming (1971).

"The most dangerous moment in programming is the very moment you become convinced you understand the program." - Gerald Weinberg

This is what epistemology calls the "illusion of understanding." When we read text, we get the feeling of having understood it without actually understanding it deeply. Code is no different.

Mechanisms that produce the readability illusion:

Countermeasures:

  1. Do not read the PR description before reading the code (prevents anchoring)
  2. Execute the core logic by hand in your head (manual tracing)
  3. Ask first, "what input would make this code fail?" (a falsificationist approach)
  4. The moment you feel you have understood, doubt yourself once more

Illusion 2: The Tests Passed = It Is Correct (The Limits of Verification)

When the whole test suite is green, we want to believe the code is correct. But this runs straight into the fundamental limits of induction.

The eighteenth-century philosopher David Hume stated the problem of induction plainly: past experience does not guarantee the future. Observing a thousand swans, all of them white, does not license the conclusion that "all swans are white" -- not until a black swan is found in Australia.

Software testing is the same.

Test resultWhat it actually meansCommon misreading
100 tests passBehaves as expected in 100 specific scenariosCorrect in every scenario
100% code coverageEvery line of code executed at least onceEvery execution path and state combination verified
Integration tests passInteraction between components confirmed in one environmentConsistency guaranteed in every environment
Performance tests passThresholds met under the test conditionsPerformance guaranteed under production load

Epistemic limits by verification method:

Illusion 3: It Works in Production = It Is Correct (The Problem of Induction)

"It has been running fine in production for two years." To an engineer, that sentence feels like the strongest justification available. But it is a textbook error of inductive reasoning.

In The Black Swan (2007), Nassim Taleb offers the "turkey problem." A turkey fed every day builds up, over a thousand consecutive days of being fed, the conviction that "the farmer is the being who feeds me." On day 1,001, at Thanksgiving, the turkey's neck is wrung. A thousand days of empirical evidence is invalidated in an instant.

The same pattern repeats in software systems.

The difference between "it works" and "it is correct":

Perspective"It works""It is correct"
Time rangeThe period observed so farAll future time included
Input rangeThe inputs received so farEvery possible input
Environment rangeThe current infrastructure and configurationEvery possible environment
Level of guaranteeEmpirical (inductive)Logical (deductive)
Philosophical statusInductive generalizationUniversal truth

Chapter 3: Cartesian Methodical Doubt and Code Review

What Is Methodical Doubt?

In Discourse on the Method (1637), René Descartes (1596-1650) put forward methodical doubt. His approach is simple but radical: doubt everything that can be doubted, and keep only what cannot be doubted.

Descartes doubted the senses, memory, and even logical reasoning, and finally arrived at the conclusion that the one thing he could not doubt was "the existence of the self that is doing the doubting" -- the famous "Cogito, ergo sum" (I think, therefore I am).

Applying Methodical Doubt to Code Review

Applying Descartes' methodical doubt to code review produces the following framework of questions.

Step 1: Doubt the premises

Step 2: Doubt the implementation

Step 3: Doubt the verification

Step 4: Doubt yourself

A Practical Checklist for Methodical Doubt

Doubt stageQuestionHow to check
Doubt the premiseIs the problem definition accurate?Cross-check against the requirements doc
Doubt the premiseAre the assumptions explicit?Check code comments and documentation
Doubt the codeAre boundary values handled?Check for boundary-value tests
Doubt the codeAre failure paths safe?Trace the error handling
Doubt the testsAre the tests meaningful?Consider mutation testing
Doubt yourselfHave I fallen into a bias?Deliberate re-examination

Chapter 4: Karl Popper's Falsificationism and Test-Driven Development

The Core of Falsificationism

Karl Popper is one of the most influential philosophers of science of the twentieth century. The falsificationism he set out in his major work The Logic of Scientific Discovery (1934) supplies a criterion for distinguishing science from non-science.

For Popper, a scientific theory must be falsifiable: there must exist some observation or experiment capable of showing the theory to be wrong. "All swans are white" is a scientific claim, because a single black swan can refute it. By contrast, "God exists" or "everything happens for a reason" cannot be falsified and therefore are not scientific claims.

TDD Is Falsificationism in Practice

Test-driven development resembles Popper's falsificationism to a striking degree.

FalsificationismTDD
Form a hypothesisDefine the functional requirement
Derive a falsifiable predictionWrite a failing test first (Red)
Test the hypothesis by experimentWrite code to make the test pass (Green)
Provisionally accept a hypothesis that survivesRefactor and confirm the tests still pass (Refactor)
Keep attempting new refutationsAdd new test cases
Revise the hypothesis when it is refutedFix the code when a test fails

Popper said that a theory can never be proved; it merely remains un-refuted. In the same way, as Dijkstra put it, tests cannot prove the correctness of code. They only maintain a state of not yet having been refuted.

Writing Falsifiable Requirements

Popper's falsificationism applies to writing requirements as well. A falsifiable requirement carries a clear test criterion inside it.

Unfalsifiable (bad) requirements:

Falsifiable (good) requirements:

Rereading Dijkstra's Warning

In his 1988 paper "On the Cruelty of Really Teaching Computing Science," Dijkstra explained the fundamental limits of software testing this way.

"Program testing can be a very effective way to show the presence of bugs, but it is hopelessly inadequate for showing their absence."

This reflects Popper's falsificationism exactly. When a test fails, the presence of a bug is confirmed irrefutably. But when a test passes, the absence of bugs is not proved. The best we can do is raise confidence in the code incrementally through more attempts at refutation -- that is, more tests.


Chapter 5: Comparison Tables - Software Verification Methods Seen Epistemically

Overall Comparison: Verification Methods and Epistemic Strength

Verification methodEpistemic typeConfidenceCoverageCostLimits
Code reviewSocial epistemology (consensus)Low to mediumDependsLowDepends on reviewer skill and bias
Unit testsInductive reasoningMediumNarrowLowMisses interaction and integration problems
Integration testsInductive reasoningMediumMediumMediumGaps caused by environmental differences
E2E testsInductive reasoningMedium to highBroadHighSlow, brittle, cannot cover every path
Static analysisDeductive reasoningHighWithin the rule setLowDetects only problems expressible as rules
Formal verificationDeductive proofVery highWithin the specVery highCannot guarantee the correctness of the spec itself
Production monitoringEmpirical observationRetrospectiveActual usage rangeMediumDetects only problems that have already occurred
Chaos engineeringExperimental refutationHighFailure scenariosHighLimited to the scenarios that were designed

The Epistemic Hierarchy of Verification Methods

Arranging verification methods in order of epistemic strength produces a pyramid.

Level 1 - Belief: the author's own conviction. "I believe this code is right." Epistemically the weakest.

Level 2 - Social consensus: agreement from peers through code review. "Our team reviewed this code and approved it." Multiple perspectives are reflected, but it is vulnerable to groupthink.

Level 3 - Empirical evidence: passing tests and production operating experience. "This code passed 1,000 tests and ran in production for six months." Strong inductive evidence, but it cannot guarantee anything about unobserved territory.

Level 4 - Logical proof: formal verification and mathematical proof. "We proved mathematically that this algorithm is correct." The strongest, but limited in applicability and very expensive.

The key insight is that most software development happens at levels 2 and 3. We work in the domain of "sufficient confidence" rather than absolute certainty, and admitting this is the starting point of epistemic humility.


Chapter 6: The Dunning-Kruger Effect and Technical Humility

The Nature of the Dunning-Kruger Effect

In 1999 the psychologists David Dunning and Justin Kruger published the results of an experiment conducted at Cornell University. Titled "Unskilled and Unaware of It: How Difficulties in Recognizing One's Own Incompetence Lead to Inflated Self-Assessments," the paper became one of the most widely cited studies in the history of cognitive bias research.

The core of the Dunning-Kruger effect is as follows.

Dunning-Kruger in Software Engineering

The effect is observed with unusual clarity in software engineering.

Career stageTypical self-perceptionActual capabilityEpistemic character
Junior (0-2 years)"I'm pretty good at this"Still learning fundamentalsUnaware of the scope of what they don't know
Mid-level (2-5 years)"There's so much I don't know"Practical skills growingBeginning to see the scope of their ignorance
Senior (5-10 years)"The more I know, the less I know"Deep expertiseAccepts and exploits uncertainty
Staff+ (10+ years)"It depends on the situation"Broad contextual judgmentAcknowledges there is no absolute answer

This is why a junior developer says "this architecture is the best" in a voice full of conviction while a senior developer says, carefully, "well, in our situation there are these trade-offs..." The senior's caution is not incompetence; it is developed metacognition.

The Value of "I Don't Know"

In software engineering culture, "I don't know" is often taken as a weakness. For a senior engineer or a lead in particular, admitting ignorance can feel like a loss of authority. Epistemically, though, "I don't know" is an honest statement of the state of one's knowledge.

Why "I don't know" has value:

  1. It starts an inquiry: the certainty of "I know" halts investigation, while admitting "I don't know" begins it
  2. It activates collective intelligence: once one person admits ignorance, others begin sharing what they know
  3. It prevents bad decisions: a careful decision that acknowledges uncertainty is safer than a confident decision built on incomplete knowledge
  4. It builds a learning culture: in an environment where "I don't know" is safe, questions and learning flourish

How to say "I don't know" productively:


Chapter 7: Bayesian Reasoning and Technical Decision-Making

The Bayesian Mindset

Bayesian reasoning, named after Thomas Bayes (1701-1761), is a method for updating the probability of a belief on the basis of new evidence. The core formula is as follows.

P(A|B) = P(B|A) x P(A) / P(B)

Here P(A) is the prior probability, P(A|B) is the posterior probability after observing the new evidence B, and P(B|A) is the likelihood.

In Thinking, Fast and Slow, Daniel Kahneman noted that humans do not perform Bayesian updating naturally. We tend to either overreact or underreact to new evidence.

Bayesian Thinking in Incident Root-Cause Analysis

When a production incident occurs, Bayesian reasoning supplies a powerful framework for root-cause analysis.

Scenario: API response time suddenly increased tenfold.

Step 1: Set the priors (based on experience and statistics)

Possible causePriorGrounds
Database load35%The most common cause of past incidents
Network problem20%Frequency of infrastructure incidents
Code deployment issue25%There was a recent deployment
External service outage15%External dependencies exist
Resource exhaustion5%Rare but possible

Step 2: Gather evidence and update the probabilities

Evidence 1: "There was a deployment within the last 30 minutes" -- the probability of a code deployment issue rises (25% -> 45%)

Evidence 2: "DB query response times are normal" -- the probability of database load falls (35% -> 5%)

Evidence 3: "Only one specific API endpoint is slow" -- the probability of a code deployment issue rises further (45% -> 70%)

Step 3: Act on the updated probabilities

Possible causeUpdated probabilityAction priority
Code deployment issue70%1st: review recent deployment changes
External service outage15%2nd: check external service status
Network problem7%3rd: check network metrics
Database load5%4th: check detailed DB monitoring
Resource exhaustion3%5th: check server resources

Bayesian Thinking in Everyday Technical Decisions

Bayesian thinking applies not only to incident analysis but to everyday technical decisions.

Technology selection:

The point is to update judgments flexibly as evidence arrives. Not fixating on the first judgment, and instead adjusting probabilities every time new information comes in, leads to better technical decisions.


Chapter 8: Nassim Taleb's Antifragility and System Design

Fragile, Robust, Antifragile

In Antifragile: Things That Gain from Disorder (2012), Nassim Taleb classified systems into three categories.

PropertyFragileRobustAntifragile
DefinitionDamaged by shocksIndifferent to shocksStrengthened by shocks
AnalogyA glassA rockA muscle
Stance toward uncertaintyAvoidanceResistanceExploitation
On failureShattersEnduresAdapts and grows

Applying This to Software Systems

Characteristics of a fragile system:

Characteristics of a robust system:

Characteristics of an antifragile system:

Design That Exploits Uncertainty vs Design That Refuses It

Design approachRefusing uncertainty (fragile)Exploiting uncertainty (antifragile)
Error handling"This error will not happen""Every error can happen"
Capacity planningBased on accurate forecastingBased on autoscaling and elasticity
Deployment strategyBig-bang deploymentCanary, blue-green, progressive rollout
Incident responseReact after the failure occursProbe in advance with chaos engineering
ArchitectureMonolithic, tightly coupledLoose coupling, circuit breakers
DataA single databaseMultiple stores, event sourcing
Team structureDependent on one expertDistributed knowledge, pair programming

Netflix's chaos engineering is the canonical example of antifragile design. Through Chaos Monkey, Netflix deliberately takes services down in the production environment. That "stress" makes the system sturdier. Rather than trying to avoid failure, they use failure to strengthen the system.


Chapter 9: Collective Intelligence and the Limits of Individual Knowledge

The View from Social Epistemology

Traditional epistemology focused on individual knowledge, but social epistemology attends to the social dimension of knowledge. Knowledge does not exist only inside an individual head; it is produced and validated through the interactions of a group.

This perspective matters enormously in software engineering. Modern software systems are too complex for one person to understand in full. So we rely on collective knowledge mechanisms.

The Epistemic Basis of Code Review

Code review is not merely a quality-control procedure. Epistemically, code review is a process of knowledge validation by multiple knowing subjects.

When one person writes code, their knowledge of that code is personal and subjective. When code review adds another person's perspective, the knowledge becomes intersubjective. That provides a stronger justification than individual belief does.

The epistemic value code review provides:

ValueDescriptionEpistemic meaning
Multi-perspective checkingEngineers with different backgrounds review the same codeReduced confirmation bias
Sharing tacit knowledgeContext and experience are shared during the reviewIncreased collective knowledge
Exposing assumptionsReviewers question assumptions the author took for grantedHidden premises verified
Distributing knowledgeUnderstanding of the code spreads from one person to the teamSingle point of failure removed

The Epistemic Role of ADRs and RFCs

Architecture Decision Records (ADRs) and the Request for Comments (RFC) process are mechanisms for raising the epistemic quality of decisions.

The epistemic functions of an ADR:

  1. Explicit justification: recording "why we made this decision" supplies the grounds of justification to your future self and your colleagues
  2. Context preservation: recording the constraints and background at the time of the decision prevents knowledge from being severed from its context
  3. Recording alternatives: recording alternatives that were considered but not adopted leaves a trace of the attempts at refutation
  4. Reversibility: it establishes the basis for reversing the decision if it turns out to be wrong

The epistemic functions of an RFC process:

  1. Criticism up front: validating the design collectively before implementation surfaces errors at the stage where they are cheap
  2. Securing diversity: gathering opinions from varied backgrounds reduces bias
  3. Evidence-based discussion: requiring grounds for claims promotes justified assertions rather than mere opinions

Chapter 10: Practical Implications - Working Epistemic Humility into Engineering Culture

Building a Culture Where You Can Say "I Don't Know"

For "I don't know" to be a safe thing to say in an organizational culture, structural mechanisms are needed.

1. Leaders model it first

When a technical leader or manager says "I'm not certain about this part either," psychological safety rises across the whole team. Google's Project Aristotle research found that psychological safety is the single most important factor in high-performing teams.

2. Establish practices for recording uncertainty

3. Build mechanisms that encourage questions

The Sprint as a Hypothesis-Testing Cycle

An agile sprint can be reinterpreted through the lens of Popper's falsificationism.

Sprint stageFalsificationist reading
Sprint planningForm the hypothesis: "if we build the feature this way, users will be satisfied"
DevelopmentMake the hypothesis concrete: express it precisely in the form of code
TestingAttempt refutation: explore scenarios in which the hypothesis could be wrong
DeploymentRun the experiment: expose the hypothesis to the real world
Review/retrospectiveAnalyze the result: was the hypothesis refuted, or provisionally accepted?

Writing Technical Documents That Admit Uncertainty

Here is a framework for handling uncertainty in technical documentation.

A confidence notation system:

NotationMeaningWhere to use it
CONFIRMEDConfirmed by testing, proof, or official documentationStating verified facts
EXPECTEDHigh confidence, but not directly verifiedInferences drawn from documentation
ESTIMATEDAn estimate based on experience and intuitionPerformance and capacity estimates
ASSUMEDAn unverified assumption, to be confirmed laterMaking assumptions explicit
UNKNOWNNot known, requires further investigationMarking uncharted territory

Applying this notation consistently across technical documents lets readers grasp the certainty level of each piece of information immediately.


Chapter 11: Checklists - Habits of an Engineer Who Practices Epistemic Humility

Daily Habits

Weekly Habits

Decision-Making Checklist

Code Review Checklist

Incident Response Checklist


Conclusion: Socrates' Wisdom, the Engineer's Humility

Socrates' confession 2,500 years ago -- "I know that I know nothing" -- is wisdom more urgently needed in software engineering today than ever.

The systems we build grow steadily more complex, more interconnected, and less predictable. In the face of that complexity, the certainty that "I understand this completely" is the most dangerous form of ignorance.

The core lessons epistemology offers software engineering are these.

  1. Knowledge is not absolute: the correctness of code can be claimed only through the absence of refutation, never through proof (Popper)
  2. Certainty conceals bias: our judgments are distorted by a range of cognitive biases (Kahneman)
  3. Uncertainty is not the enemy but information: exploit uncertainty and the system grows stronger (Taleb)
  4. The wisdom of the group beats the certainty of the individual: code review, ADRs, and RFCs are not luxuries but epistemic necessities

As Gerald Weinberg said fifty years ago, programming is ultimately a psychological activity. And epistemic humility is the most fundamental psychological quality for building better software.

"True knowledge is knowing your own ignorance." - Socrates

Technical humility is not a weakness. It is the most powerful engineering principle for surviving in a world of complexity and uncertainty.


References

  1. Karl Popper, The Logic of Scientific Discovery (1934) - the basic principles of falsificationism and scientific methodology
  2. Edsger Dijkstra, "On the Cruelty of Really Teaching Computing Science" (1988) - the essential limits of software testing
  3. Daniel Kahneman, Thinking, Fast and Slow (2011) - cognitive bias and the psychology of decision-making
  4. Nassim Taleb, Antifragile: Things That Gain from Disorder (2012) - designing systems that exploit uncertainty
  5. David Dunning and Justin Kruger, "Unskilled and Unaware of It" (1999) - the original paper on the Dunning-Kruger effect
  6. Gerald Weinberg, The Psychology of Computer Programming (1971) - the psychological side of programming
  7. Nassim Taleb, The Black Swan (2007) - extreme uncertainty and the limits of prediction
  8. Google Research, "Project Aristotle" - psychological safety as the key factor in high-performing teams

Comments

No comments yet.

Sign in to leave a comment