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:

TestInputsExpected
Areadings 80, 90, 100EXCELLENT
Breadings 50, 60, 70NORMAL
Creadings 10, 20, 30WARNING
Dcount 0graceful 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:

  1. rerun the test that exposed it;
  2. rerun previous passing tests;
  3. 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 for loop;
  • 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.