Debugging and Root Cause Fixing
Core Philosophy
Find and fix the root cause. NEVER work around problems with wrappers or complexity.
Debugging Workflow
- Read the Stack Trace
Stack traces read bottom-to-top. The last frame is where error occurred.
Traceback (most recent call last): File "main.py", line 10, in <module> # Entry point result = process_data(data) # Calling function File "processor.py", line 25, in process_data return transform(item) # Where it failed TypeError: 'NoneType' has no attribute 'items' # What went wrong
Key info: File, line number, function name, actual error message.
- Reproduce the Bug
Create minimal reproduction
pytest tests/test_module.py::test_specific -v
Or write a quick script
python -c "from module import func; func(problematic_input)"
- Use Debugger or Print Statements
Quick debugging with print (remove after!)
print(f"DEBUG: {variable=}, {type(variable)=}")
Better: Use debugger
import pdb; pdb.set_trace() # Interactive debugger
Or breakpoint() in Python 3.7+
breakpoint()
- Bisect if Needed
Find which commit introduced the bug
git bisect start git bisect bad HEAD git bisect good v1.0.0
Git will guide you through testing commits
The Anti-Pattern: Working Around Instead of Fixing
When you encounter an error, your instinct may be to:
-
Add a wrapper class
-
Use getattr for dynamic delegation
-
Use # type: ignore to suppress errors
-
Create helper classes that "adapt" interfaces
This is WRONG. These are signs you're working around instead of fixing.
Common Workarounds to Avoid
Wrapper Classes with getattr
WRONG - Magic delegation
class TestWrapper: def init(self, obj): self.obj = obj def getattr(self, name): return getattr(self.obj, name)
CORRECT - Simple helper function
def test_helper(obj): pass
Adapter Classes
WRONG - Adapter to hide interface mismatch
class TestAdapter: def init(self, production_obj): self.obj = production_obj def adapted_method(self): return self.obj.method().to_dict()
CORRECT - Transform inline, fix the interface
def test_something(production_obj): result = production_obj.method() assert result.to_dict()["field"] == expected
Type Ignores to Hide Problems
WRONG - Hiding the problem
result = maybe_none.items() # type: ignore
CORRECT - Handle the None case
if maybe_none is not None: result = maybe_none.items()
Red Flags
You're working around instead of fixing if you're:
-
Creating a wrapper class "just for tests"
-
Using getattr or other magic methods
-
Adding complexity to avoid changing existing code
-
Thinking "I'll just adapt this interface..."
-
Using # type: ignore without investigating why
The Right Approach
-
Stop when you realize you're working around
-
Identify the root cause (read stack trace, reproduce)
-
Fix the root cause with simple, explicit code
-
Delete any workarounds you created
Questions to Ask
-
Am I adding complexity to avoid fixing the real problem?
-
Is there a simpler, more direct way to do this?
-
Would someone reading this code understand what's happening?
-
Can I reproduce this bug with a minimal test case?
Related Skills
-
/pythonista-testing - TDD for bug fixes
-
/pythonista-typing - Type safety
-
/pythonista-patterning - Pattern discovery