Skip to content
Back to articles

Computing education · Pedagogy

Debugging as
a teachable habit

Why errors are information, not embarrassment.

On this page
  1. What you will take away
  2. Why debugging matters
  3. The habit
  4. Classroom norms
  5. A worked example
  6. Common traps
  7. What to teach next
  8. Try it yourself
  9. Related reading
  10. References

When a program fails, many pupils apologise. Some go quiet. A few ask for the answer immediately.

That reaction is understandable. It is also a missed lesson.

Debugging is not what happens when teaching ends. It is part of what computing is: forming a hypothesis about how code should behave, comparing that expectation to reality, and updating your model when they disagree.

If we treat errors as embarrassment, we teach pupils to hide the most useful moments in the work. If we treat debugging as a habit, we teach them to think like programmers.

What you will take away

By the end of this article, you should be able to:

  • teach debugging as a repeatable reasoning process;
  • distinguish a useful hypothesis from random code changes; and
  • design classroom routines that treat failures as evidence.

Why debugging matters

Working programs are not the only output worth marking. The ability to repair a program is evidence of understanding:

  • Can the pupil localise the fault?
  • Can they explain what they expected?
  • Can they test a change without breaking something else?

These skills transfer across languages, tools, and contexts. They also connect computing to wider scientific thinking: observe, hypothesise, test, revise. Research on debugging instruction similarly distinguishes language, program, procedural, strategic, and experiential knowledge rather than treating repair as one generic skill (Yang et al. (opens in a new tab)).

This complements teaching computing beyond syntax: syntax gets code onto the page; debugging shows whether the pupil owns the idea.

The habit

A simple five-step habit works from Year 7 to undergraduate level:

  1. Reproduce. Find an input or action that makes the failure happen again.
  2. Narrow. Remove code, simplify data, or print intermediate values until the fault has a smaller scope.
  3. Hypothesise. State in plain language what you think is wrong.
  4. Test one change. Alter a single thing and observe the effect.
  5. Record. Note what you tried, even briefly — so you do not repeat dead ends.

This is not a linear checklist every time. Pupils loop between steps. The point is to make the loop visible and routine, not to pretend debugging is mysterious intuition.

Classroom norms

Habits stick when the classroom rewards them.

Useful norms:

  • “Interesting — what does that error tell us?” instead of “That’s wrong.”
  • Predict before run. Pupils state expected output; the debugger compares prediction to result.
  • Pair debugging. One reads, one types, both explain the next step.
  • Public bug surgery. Occasionally work through a failing program together, modelling calm reasoning under uncertainty.
  • Credit the trace. Mark short debugging notes alongside final code.

Avoid norms that undermine the habit: rushing to the corrected file, treating help as failure, or marking only green ticks on automated tests with no explanation.

A worked example

A pupil writes:

def average(values):
    total = 0
    for value in values:
        total += value
    return total / len(values)

average([2, 4, 6]) returns 4.0 — correct. average([]) raises ZeroDivisionError.

A weak response: give the corrected function.

A debugging-habit response:

  1. Reproduce: empty list triggers the error.
  2. Narrow: the loop is fine; the division uses len(values).
  3. Hypothesise: dividing by zero when the list is empty.
  4. Test: guard with if not values: return 0 or raise a clear error — discuss which behaviour matches the problem definition.
  5. Record: “Empty input needs a policy before division.”

The lesson is not the guard clause alone. It is the reasoning path that makes the guard meaningful.

Common traps

Teachers should name traps pupils fall into:

  • Random edits. Changing code without a hypothesis wastes time and creates new bugs.
  • Print spam. Unstructured output without a question to answer.
  • Copy-paste fixes. Borrowing a solution without understanding the failure.
  • Giving up at the first error message. Messages are clues, not verdicts.
  • Debugging only at the end. Testing edge cases early prevents larger failures.

Each trap is a chance to redirect pupils back to the habit, not to criticise their character.

What to teach next

Debugging pairs naturally with other computing habits: tracing execution, writing small tests, comparing solutions, and explaining design choices.

If you teach the subject, consider:

  • Where do your tasks allow pupils to see errors as useful data?
  • What would you accept as evidence of debugging — not only a final correct program?
  • Which failure in your next lesson is worth slowing down for?

The goal is not more broken programs. It is more pupils who know what to do when programs break — which, in practice, is most of the job.

Start with one routine: predict, run, compare, explain the gap. Build from there.

Try it yourself

Take a short program that fails on one edge case. Before changing it, write down the smallest input that reproduces the failure, one hypothesis, and one observation that would disprove that hypothesis.

Answer

There is no single correction because the exercise is about method. A strong response contains three checkable statements:

  1. an input that makes the same failure happen reliably;
  2. a specific proposed cause, not merely “the code is wrong”; and
  3. a test whose result could show that the proposed cause is false.

Only then should you make one change and compare the result with the prediction.

References

  1. Yang et al., "Decoding Debugging Instruction" (ACM Transactions on Computing Education) (opens in a new tab)
More writing