Systematic Debugging

Lecture 10: Systematic Debugging Fall 2025 - From original slides by Drs. John Keyser, Tony Cahill & Niki Ritchey, updated Fall 2025 by Dr. Kristin Schaefer


Overview of Topics Covered

  • Types of errors

  • Catching errors

  • The debugging process

  • The use of the debugger in the IDE


Types of Errors

  • General Statement: Everyone makes errors when coding. Better programmers may make fewer errors.

    • Research Findings: Studies show that better programmers not only make fewer errors but are significantly faster at identifying and correcting them.

1. Syntax Errors

  • Definition: Occur when incorrect syntax is used in the code.

  • Detection: Usually caught by the code editor before execution.

  • Impact: Most syntax errors prevent the code from running altogether.

  • Ease of Resolution: Relatively easy to find and fix.

  • Examples:

    • whil x > 3: # misspelled keyword (should be while)

    • if x = 2: # used assignment operator = instead of equality operator ==

    • def dosomething(x): ... dosomething() # missing parameter for function call.

2. Run-Time Errors

  • Definition: Occur during execution of the program, often referred to as exceptions.

  • Characteristics: Not predictable ahead of time.

    • Note: Due to Python being an interpreted language, some syntax errors might not be recognized until run-time.

  • Common Causes:

    • Programs exhausting memory limits.

    • Excessive nested function calls, particularly due to recursion.

  • Examples:

    • answer = int(input('Enter a number: ')) # fails if non-integer input.

    • a = my_list[20] # fails if the list has fewer than 21 elements.

    • c = a / b # fails if b is zero.

    • L = [1] while True: L += [1] # leads to excessive memory usage due to infinite list growth.

3. Logic Errors

  • Definition: Mistakes in program logic leading to incorrect results.

  • Characteristics:

    • Code executes without errors but produces wrong output.

    • The error is often not located near the source of the logical mistake.

  • Complexity: These are often the hardest errors to debug since they involve reasoning about the algorithm or logic.

  • Examples:

    • myanswer = 4 # should have been my_answer.

    • if x > 2: # intended condition should have been x >= 2.

    • Indentation errors that mislead program execution.

Causes of Logic Errors

  • Typos: Accidental misspellings or incorrect variable names.

  • Misunderstandings: Incorrect assumptions about how certain code constructs work.


Handling Run-Time Errors

  • Approach: While some run-time errors are unpredictable, certain structures can help minimize issues:

    • try-except Statement: A Python construct designed to catch exceptions and prevent the program from crashing.

    • Basic Idea: Attempt to execute code, handle exceptions if they arise, and exit gracefully.

    • User Interaction: Can be used to prompt users for corrected input when an error occurs.

    • Example:
      python try: # code to run except <exception_type>: # code to handle exception

  • Structure of the try-except Statement:

    1. Start with the keyword “try” followed by a colon.

    2. Indent the code that should be executed.

    3. If an exception occurs, proceed to the except block which includes an .

Exception Types
  • Commonly encountered exception types include:

    • TypeError: Attempts to perform an operation on an incompatible type.

    • IndexError: Attempting to access an element not within the range of a list.

    • ZeroDivisionError: Occurs when dividing by zero.

    • NameError: Accessing a variable that has not been defined.

  • Example Usage:

try:
    a = int(input("Enter a numerator: "))
    b = int(input("Enter a denominator: "))
    c = a / b
except ZeroDivisionError:
    print("You can't divide by 0!")
    b = int(input("Enter a different denominator: "))
    c = a / b  # potential error again
print(c)

Catching Errors

  • Importance: Utilizing try-except statements can facilitate continued program execution and provide diagnostic information. However, they do little to confront bugs effectively.

The Concept of Bugs

  • Historical Reference: The term 'bug' in computing is attributed to Thomas Edison with respect to a telegraph improvement. Additionally, Grace Hopper highlighted the first computer bug found in 1947, where a moth was discovered in the Mark II computer relay.

  • Definition: In computing, a bug refers to any error or problem resulting from faulty code or logic.


Debugging Process for Inexperienced Programmers

  • Typical steps include:

    1. Identify something that doesn't work.

    2. Make random changes to the code.

    3. Verify if the change resolved the issue.

A More Structured Debugging Process: DRIFT

  1. Discover: Identify a repeatable problem.

  2. Reproduce: Create a test case that consistently demonstrates the incorrect behavior.

  3. Isolate: Pinpoint where in the code the issue originates.

  4. Fix: Implement a solution.

  5. Test: Validate the fix and ensure no new issues arose during correction.


Using an Interactive Debugger

  • Definition: Code editors often include a debugger, which assists in navigating and inspecting code through its execution.

    • Examples of IDEs with debuggers: VS Code, Spyder, and PyCharm.

  • Role of Debugger: Does not fix bugs automatically, but provides features to assist debugging:

    • Execution Control: Set breakpoints, step through code, and continue execution.

    • Examination Tools: Inspect memory values and call stacks during execution.

Execution Control Tools Available
  • Breakpoints: Designate specific lines in code where execution pauses for inspection.

  • Step/Step-over: Progresses to the next line of code.

  • Step-into: Goes into the function being called at the current line of code.

  • Continue/Run: Executes until the next breakpoint is hit.

Examination Tools Available
  • Call Stack: Displays the sequence of function calls leading to the current point in execution.

  • Variable Explorer/Watch-List: Lists current variables and their values, potentially with hierarchical data representation.

Debugging Approach

  • Set a breakpoint near the expected area of error, run the program to that breakpoint, examine variable values, step through code to identify logic faults.


Debugging without an IDE

  • Print Statements: Often the easiest method when debuggers aren’t available. Use print statements to output variable states and flow at critical points.

Alternative Methods

  • Rubber Duck Debugging: Explaining the code line-by-line to an inanimate object (the 'duck') helps clarify logic and identify errors.

  • Assert Statement: Used to conduct sanity checks throughout the code, where assertions evaluate conditions and raise exceptions if failed.

    • Structure: assert <expression>, <arguments>

    • Example: assert a >= 0, "a is not positive"


Summary of Debugging Principles

  • Treat the debugging process systematically through the DRIFT framework. It emphasizes repeatability, isolation, fixation, and validation of fixes. All findings should be scrutinized for potential similar issues elsewhere in the code.

Application Beyond Programming

  • Debugging principles can also be applied to real-world problems, such as machinery, by following similar steps: identifying the issue, isolating it, fixing it, and validating the solution.


Practical Debugging Exercise

  • A specific task will be conducted to identify and eliminate bugs within a provided codebase, demonstrating the complete debugging process, leveraging the available tools from the IDE.