Back to Devoured - September 25, 2026
DEVOURED
Not Everyone Can Code

Not Everyone Can Code

Tech Joe Marshall
Effective debugging requires advanced counterfactual reasoning—a cognitive skill that roughly 40% of adults find deeply unnatural and frustrating.
What: Joe Marshall argues that debugging is essentially the construction of counterfactuals ('Why did this happen instead of that?'). He contends that those who cannot reason beyond observable facts will struggle to move past surface-level coding and fail to handle complex concurrency issues.
Why it matters: This challenges the 'everyone should code' narrative by suggesting that the mental architecture required for deep system-level engineering is cognitively distinct and not universally present.
Deep dive
  • Counterfactual reasoning involves evaluating 'what if' scenarios that deviate from the actual state.
  • Judea Pearl defines three levels of reasoning: Association, Intervention, and Counterfactuals.
  • Many programmers survive by pattern matching and using LLMs, effectively sticking to Levels 1 and 2.
  • Multithreaded debugging requires managing an exponential state space of possible interleavings.
  • Concurrency is described as debugging a 'multiverse' rather than a single machine.
  • The cognitive burden of holding multiple contradictory realities in mind is a core differentiator for senior engineers.
Decoder
  • Counterfactual reasoning: The process of considering scenarios that did not actually occur to understand causality.
  • Abductive reasoning: A form of logical inference that starts with an observation and seeks to find the simplest or most likely explanation.
  • Interleaving: In concurrent systems, the specific order in which instructions from different threads are executed by the CPU.
Original article

Not Everyone Can Code

Not everyone can debug code

Counterfactual reasoning is a technique where you assume alternative scenarios that could have happened, but did not in fact happen. You assume something is true when it is, in fact, false. A question involving a counterfactual is "What if dinosaurs had not gone extinct?" Reasoning with counterfactuals is considered an advanced cognitive skill, and it comes in quite handy when you are debugging code.

To debug effectively, you have to entertain insulting falsehoods. You must look at your own logic and ask: "Suppose this invariant I spent three days designing failed. Suppose this pointer is null, even though I checked it two lines ago. How does that world exist?" You have to temporarily decouple the state of the machine from your own intentions.

A surprising proportion of the adult population has extreme difficulty with counterfactual reasoning. They get actively frustrated or hostile when asked to reason from a premise they know to be untrue. Psychological studies (Dulit 1972, Keating 1979, Neimark 1975) estimate that roughly 20% of adults experience severe difficulty with counterfactual thinking, and another 20% experience moderate difficulty. This means that 40% or more of people are fundamentally anchored to the concrete, observable world. These people are not unintelligent; they simply possess a cognitive architecture that rejects hypothetical fictions. But because software state is entirely abstract, they will find diagnosing bugs an agonizing, borderline impossible task.

In causal inference, Judea Pearl laid out three levels of reasoning:

  1. Association: What is? What is the correlation between X and Y?
  2. Intervention: What if? What happens to Y if we intervene and set X to x?
  3. Counterfactuals: Why? Why did Y happen when X was x?

A programmer who cannot entertain counterfactuals will be able to answer the first two questions, but not the third. They won't be able to debug any but the most trivial programs.

Debugging is pure counterfactual reasoning. It isn't just observing the present, it is abductive reasoning over hypothetical pasts. You have the actual world: The pointer is null. You must construct a counterfactual world: In order for the pointer to be null, the allocation must have failed, or the loop must have been skipped, etc. If someone cannot reason counterfactually, they cannot diagnose a bug deductively.

This is not to say that someone bad at counterfactual reasoning cannot appear to be a programmer. You see them all the time. They don't simulate the system in their head, they do pattern matching. They copy a boilerplate framework or prompt an LLM or crib some code from StackOverflow (if it still exists). They glue libraries together using syntactic recipes. They operate strictly at Pearl's levels 1 and 2, association and intervention, but never reach level 3, counterfactuals.

Not everyone can debug multithreaded code

Reasoning about multithreaded code is counterfactual reasoning on steroids. In a single-threaded program, you trace a single path of what happened versus what might have happened. In concurrent systems, you must simultaneously evaluate an exponential explosion of alternate histories. You have to reason about every possible interleaving of thread execution, asymmetric cache invalidations, and preemption points that could occur, even if they have never yet been observed in testing.

To the concrete mind, if a race condition does not manifest across a thousand test runs, it does not exist. To the concurrency engineer, the untested interleaving is a lurking ghost. You are not debugging a machine; you are debugging a multiverse. It requires maintaining a swarm of mutually contradictory realities in working memory all at once—a cognitive burden so unnatural that very few humans can do it well without the aid of formal models or pure architectural restraint.

Regardless of how you feel about the slogan "Everyone should learn to code", some people will have vastly harder times than others.

Explore all 50 articles in today's digest

Fresh dev news, AI-digested daily. Never miss a trend at devoured.fyi