---
title: "Debugging as a teachable habit — Erkan Malcok"
description: "Debugging is not a remedial afterthought. This piece treats it as core computing thinking: reproduce, narrow, hypothesise, test, and learn from failure."
date: "2026-09-01"
updated: "2026-09-02"
canonical: "https://erkanmalcok.com/articles/debugging-as-a-teachable-habit/"
kind: "Opinion"
series: "computing-education"
tags:
  - "Computing education"
  - "Pedagogy"
---

# Debugging as a teachable habit — Erkan Malcok

Why errors are information, not embarrassment.

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.](https://doi.org/10.1145/3690652)).

This complements
[teaching computing beyond syntax](/articles/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:

```python
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.



## Related reading

- [Teaching computing beyond syntax](/articles/teaching-computing-beyond-syntax/)
  places debugging inside a wider account of computing understanding.
- [Recursion without mystery](/articles/recursion-without-mystery/) applies
  small-input tracing to recursive programs.

## References

- [Yang et al., "Decoding Debugging Instruction" \(ACM Transactions on Computing Education\)](https://doi.org/10.1145/3690652)
