Learn the concept
Debugging begins with a reproducible case. Reduce the input until the failure is understandable, inspect intermediate values, and verify one hypothesis at a time. Random edits can hide the problem without explaining it.
Logging records events during normal operation. Use levels to distinguish routine information from actionable failures. Include a request or operation ID so related events can be connected, but avoid logging credentials or unnecessary private input. A useful error log identifies the failed stage and outcome.
A debugger lets you pause at a breakpoint and inspect state. Assertions are useful in tests and internal invariants; do not rely on them for validating untrusted inputs because optimized execution can remove them. Keep a failing test after the bug is fixed.
Run and inspect
import logging
logger = logging.getLogger("document_pipeline")
def count_valid(records):
count = sum(1 for record in records if record.get("active") is True)
logger.info("batch_processed count=%s", count)
return count
assert count_valid([{"active": True}, {"active": False}]) == 1
Your exercise
Introduce an off-by-one bug in a batch counter. Create the smallest failing test, inspect it with a breakpoint, fix it, and preserve the regression test.
Check your understanding
You can explain the cause, show the failing and passing test, and confirm no sensitive payload is written to logs.