Valid arguments: modus ponens, modus tollens, and the fallacies that fool debuggers
A valid argument's form guarantees the conclusion whenever the premises hold; soundness adds true premises. Modus ponens and modus tollens are the two safe moves; affirming the consequent and denying the antecedent look similar — and are how debugging hunches go wrong.
It is 11 p.m. on release night. The team just enabled a new caching layer and someone pulls up the latency dashboard: 40 milliseconds, beautifully low. “If the cache works, latency is low. Latency is low. The cache works — ship it.” Everyone goes to bed. At 9 a.m. the site melts under morning traffic, and the postmortem finds the cache was never wired in at all: the feature flag was off. Latency had been low at 11 p.m. for a much more boring reason — almost nobody was using the site. The team’s reasoning felt airtight, but it had the exact shape of one of the oldest mistakes in logic, a mistake with a two-thousand-year-old name. A perfectly safe version of the same inference was sitting right next to it.
After this lesson you can define validity and soundness separately, state modus ponens and modus tollens, identify the two fallacies that mimic them, and explain why a green dashboard proves nothing about the underlying cause.
An argument is premises plus a conclusion; validity is about form, not content. In logic an argument is a list of premises (things you assume or observe) and one conclusion (the thing you claim follows). The release-night argument: premises — “if the cache works, latency is low” and “latency is low” — conclusion — “the cache works.” An argument is valid when its shape makes it impossible for all premises to be true while the conclusion is false. Validity is a property of the form, not the topic: the same shape works for any subject matter.
Soundness requires valid form AND true premises. Validity says nothing about whether the premises are actually true. An argument is sound when it is valid and all its premises are true. “All deploys on Friday fail; today is Friday; therefore today’s deploy fails” is perfectly valid — and useless, because the first premise is false. Valid form plus false premises proves nothing about the world. In a debugging session you need both.
Modus ponens and modus tollens are the two safe inference moves. Both start from a conditional P → Q:
- Modus ponens (“the affirming mode”): from P → Q and P is true, conclude Q. The cache works, latency is low; the cache works (verified) — therefore latency is low. You confirmed the if part, so the then part follows.
- Modus tollens (“the denying mode”): from P → Q and Q is false, conclude ¬P. If the cache works, latency is low; latency is high — therefore the cache is not working. This is the skeleton of almost every real debugging step: if my hypothesis were true, I would see X; I do not see X; the hypothesis is dead.
Two fallacies mimic the valid forms but prove nothing. Both use the same ingredients — a conditional and two facts — just combined in the wrong order:
- Affirming the consequent: from P → Q and Q is true, conclude P. Release night: “latency is low, therefore the cache works.” Invalid — Q can be true while P is false (low traffic, warm database).
- Denying the antecedent: from P → Q and P is false, conclude ¬Q. “The cache is broken, therefore latency must be high.” Also invalid — breaking one cause of low latency does not outlaw every other cause.
A conditional P → Q gives you traction in exactly two cases: P confirmed or Q refuted. The other two observations license nothing.
A debugging chain using modus tollens.
Hypothesis: the bug is in the parser. Rule: if the bug is in the parser, the failing input would be malformed in the logs.
Check: the logs show well-formed input.
Apply modus tollens: Q (malformed input in logs) is false, therefore P (bug in parser) is false. The parser hypothesis is dead.
New hypothesis: the bug is in the authentication middleware. Rule: if the authentication middleware is broken, all endpoints fail, not just one.
Check: only the /checkout endpoint fails; other endpoints respond normally.
Apply modus tollens: Q (all endpoints fail) is false, therefore P (auth middleware broken) is false. Auth middleware is cleared.
Each failed prediction validly eliminates a suspect. Good debugging is the disciplined accumulation of ¬P after ¬P until one hypothesis remains.
Note: when a prediction succeeds — the parser test shows malformed input — you have only affirmed a consequent. The hypothesis survived; it was not proved. Design checks whose failure is informative, not only whose success is comfortable.
▸Why this works
Why obsess over form instead of content? Because form is the only part you can check mechanically, without knowing anything about caches or weather. A valid form is a contract: feed it true premises and it is guaranteed to output a true conclusion, for any subject matter whatsoever. That guarantee is what lets a compiler’s type checker, a database’s query planner, and your own 3 a.m. debugging brain reuse the same handful of inference rules everywhere. Content can lie to you; a valid form never does — it can only be fed lies.
Name the inference rule: from (P → Q) and P, conclude Q.
Name the inference rule: from (P → Q) and NOT Q, conclude NOT P.
Is this valid? From 'if it rains, the street is wet' and 'the street is wet', conclude 'it is raining'. Type yes or no.
An argument is valid but its premise is false. Is it sound? Type yes or no.
A debugging test shows the expected bad symptom IS present. Does this prove your hypothesis? Type yes or no.
The rule holds: if the cache works, latency is low. Monitoring shows latency is LOW. What can you validly conclude about the cache?
An argument earns the word valid when its form forbids the fatal combination — all premises true, conclusion false — regardless of subject matter; it earns the word sound only when the form is valid and the premises are also actually true. A conditional rule P → Q supports exactly two valid moves: modus ponens (P confirmed, conclude Q) and modus tollens (Q refuted, conclude ¬P), and the second is the skeleton of debugging — if my hypothesis were true I would see X, I do not see X, the hypothesis is eliminated. The two remaining observations are traps: affirming the consequent (Q is true, so P must be — the release-night cache fallacy) and denying the antecedent (P is false, so Q must fail too). A green dashboard or a passing test only means your hypothesis survived, not that it is true; design checks whose failure would be informative, accumulate valid refutations, and keep validity and soundness separate in code review — a beautiful inference from a false premise proves exactly nothing.
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.
Apply this
Put this lesson to work on a real build.