Base CS from zero
When a program fails: hunt two bugs by reasoning
Reading about failures is not the same as hunting one down. Build a small program, plant two failures of opposite character — a loud crash and a silent wrong answer — then use the unit’s method to locate each cause by reasoning, not guessing, and prove the fix with evidence.
Turn the unit’s mental model into a repeatable engineering loop: distinguish a loud defined error from a silent failure in your own code, read a real stack trace top-down to the throw site, separate where an error surfaced from where its cause lives, and locate each bug by a written hypothesis confirmed against actual state.
Build a small program with two deliberately planted bugs — one that raises an exception (a loud, defined error) and one that produces a wrong answer with no error at all (a silent failure) — then locate and fix each by reasoning, documenting the hypothesis and the evidence at every step.
- A short write-up that, for each bug, states the hypothesis, the state you checked, the observation, and the one-line fix — proving you located the cause by reasoning, not by changing lines until the symptom vanished.
- For bug A: the annotated stack trace with the throw site and entry clearly marked, and a sentence on whether the throw site was the cause or whether the real cause was upstream in the call chain.
- For bug B: a clear statement of why no exception was raised — what made the failure silent — and the before/after output showing the wrong answer becoming the right one.
- A one-line classification of each bug as a defined error or a silent failure, with the reason.
- Add a third bug that is an out-of-bounds read, and describe how the same bug would behave as a defined error in a bounds-checked language versus undefined behaviour in a low-level unchecked one.
- Wrap bug A's throwing call in a handler (a catch block) one frame up, recover gracefully, and show the program now continues instead of crashing — demonstrating that an exception is not the same as the program being broken.
- Convert the silent failure into a loud one by adding a validation check that throws on the bad value, and argue in two sentences why turning a silent failure into a loud, defined error is usually an improvement.
- Write a one-page debugging runbook for a beginner: the three moves (narrow the range, read the trace, check state), how to read a trace top-down, and the rule that the throw site is where the error surfaced, not always where the cause lives.
This is the loop you will run on every real failure: classify it — loud defined error or silent wrong answer — read the trace top-down to the throw site, then separate where it surfaced from where the cause lives. Locate the cause by reasoning over a deterministic machine: write a testable hypothesis, narrow the range, check the actual state, and change a line only once you have proven the guilty step. Doing it once on a toy program with two planted bugs makes the production version muscle memory.