Unit 07 · lesson
Investigate the Broken System Console
Core path: 35 minutes
You have inherited a broken Python program.
You are not allowed to rewrite it from scratch.
That rule matters because debugging is not the same skill as replacing code until the symptom disappears. Your job is to reproduce the failure, collect evidence, form a hypothesis, make one controlled repair, and verify the result.
Save this as console.py:
def calculate_average(total, count):
return total / count
def determine_status(average):
if average >= 50:
return "NORMAL"
elif average >= 80:
return "EXCELLENT"
else:
return "WARNING"
def display_report(name, average, status):
print()
print("=== SYSTEM REPORT ===")
print(f"System: {name}")
print(f"Average: {average:.1f}")
print(f"Status: {status}")
system_name = input("System name: ")
reading_count = int(input("Number of readings: "))
total = 0
for number in range(reading_count):
reading = input(f"Reading {number + 1}: ")
total += reading
average = calculate_average(total, reading_count)
status = determine_status(average)
display_report(system_name, average, status)
Reproduce before touching the code
Run these cases first:
| Test | Inputs | Expected |
|---|---|---|
| A | readings 80, 90, 100 | EXCELLENT |
| B | readings 50, 60, 70 | NORMAL |
| C | readings 10, 20, 30 | WARNING |
| D | count 0 | graceful rejection |
Record what actually happens.
If the first run crashes, that is not permission to skip the other cases forever. Repair enough to continue, then return to the test matrix.
Create a bug record for every distinct problem
Use this structure:
## Bug 01
Reproduction:
Expected:
Actual:
Category:
Evidence:
Hypothesis:
One change:
Verification:
The important fields are Evidence, Hypothesis, and Verification.
"It broke" is not evidence.
"I changed a bunch of stuff and now it works" is not a debugging method.
Repair one bug at a time
There are multiple problems in the starting program.
For example, input() returns strings. That fact matters when the program reaches:
total += reading
Do not just add int() because you remember Week 3. Inspect reading and total first. Prove what types they contain.
After each repair:
- rerun the test that exposed it;
- rerun previous passing tests;
- record whether anything regressed.
A fix that repairs Test A while breaking Test B is not finished.
Use the debugger as an inspection tool
Set at least two breakpoints:
- inside the
forloop; - inside
determine_status().
At the loop breakpoint, inspect:
number
reading
total
At the status breakpoint, inspect:
average
Do not just click Continue until the program ends. Step far enough to answer a question about state.
Trace one case manually
Use:
reading_count = 3
readings = 10, 20, 30
Trace:
iteration 1 → total before → reading → total after
iteration 2 → total before → reading → total after
iteration 3 → total before → reading → total after
average →
status path →
Compare your manual trace with the debugger.
If they disagree, one of your mental models is wrong. That is useful information.
Find the logic bug that does not crash
Look carefully at:
if average >= 50:
return "NORMAL"
elif average >= 80:
return "EXCELLENT"
What happens when average = 90?
The program enters the first branch because 90 >= 50 is true. The later >= 80 condition never gets a chance.
This is why debugging cannot mean "look for red error text." Logic bugs often execute cleanly.
Finish cleanly
Before calling the program repaired:
- remove temporary debug prints;
- keep the useful test cases;
- verify all required cases;
- make sure expected input does not produce tracebacks; and
- preserve your bug records.
The repaired file is only half the artifact. The evidence of how you got there is the other half.