Saltar al contenido principal
The zero-to-master on-ramp — Git, the command line, a systems language, and the tooling every engineer uses daily.

Foundations & Tooling

The zero-to-master on-ramp — Git, the command line, a systems language, and the tooling every engineer uses daily.

Testing & Debugging

Follow the workflow from expectation to root cause

Good debugging is a methodical loop of proving behavior, observing execution, inspecting state, and fixing the underlying cause.

Current step

Artifact
What happens here

    Testing & Debugging Fundamentals

    Elementary (2/5) ~3–4 hours Tests Assertions Debugging Logs Breakpoints Tracebacks Prereqs: Shell & Command-Line, Systems Programming Languages (C, Rust, Go)

    Why Testing Matters

    Writing code is only half the job. The other half is proving that the code behaves as intended.

    In production, the cost of a bug is not just a failing test — it is lost time, broken trust, and surprise outages. The best engineering teams treat testing as a default part of the workflow, not a heroic last-minute task.

    A good mental model is this:

    • Testing asks, “Did I break the expected behavior?”
    • Debugging asks, “Why did this fail?”

    They are related, but they are not the same.

    Unit Tests and Assertions

    A unit test checks one small piece of behavior in isolation. The structure is simple:

    1. arrange the data,
    2. invoke the function,
    3. assert the expected result.

    Example in a generic form:

    def test_addition():
        assert add(2, 3) == 5

    The purpose of an assertion is not to be dramatic — it is to encode a rule. If the requirement changes, the test changes with it. Over time, the test suite becomes a living specification.

    Logging and Observability

    Logs are the first layer of observability. They answer everyday questions such as:

    • Did this code run at all?
    • What input did it receive?
    • What branch was taken?
    • What value changed at this step?

    Good logs are specific, structured, and useful at the moment of failure.

    echo "starting job" >> app.log
    python app.py 2>&1 | tee debug.log

    A common mistake is logging everything without context. The better habit is to log the decisive facts: input, state transition, error class, and relevant identifiers.

    Debugging from Symptoms to Root Cause

    Debugging is a process of narrowing uncertainty. The best workflows are systematic:

    1. reproduce the problem,
    2. capture the exact symptom,
    3. isolate the smallest failing case,
    4. test a hypothesis,
    5. fix the root cause rather than the visible symptom.

    A stack trace is often the first useful clue. A traceback or crash report tells you where the program was when it failed. Breakpoints and step-through debugging then let you inspect the observed state at that moment.

    Breakpoints, Stepping, and Inspection

    A debugger lets you pause execution and inspect the values that matter:

    • local variables,
    • function arguments,
    • object state,
    • call stack,
    • and control flow.

    This is especially powerful for issues that only happen under certain conditions or with certain inputs. A debugger improves the quality of your reasoning because you can test assumptions instead of guessing at them.

    The Difference Between Debugging and Observing

    A lot of “debugging” is actually just observing. The distinction matters:

    • Observing means collecting information.
    • Debugging means turning that information into a diagnosis.

    A log line may tell you something happened. A debugger helps you understand why it happened. Together they form a complete workflow.

    Practice Trajectory

    1. Write a small test for a function with a clear input and expected output.
    2. Add one assertion that fails on purpose, then fix it and re-run the test.
    3. Introduce a log line around a branch or state transition and verify it appears in the output.
    4. Reproduce a bug and inspect its traceback or stack trace.
    5. Use a debugger to pause at a line of code and inspect variable values before fixing the cause.

    When It’s the Right Tool

    SituationTakeaway
    Catching regressions earlyTests give fast feedback before a change ships
    Investigating a failureLogs and traces reduce guesswork
    Complex control flowBreakpoints make hidden state visible
    Production incidentsGood debugging habits turn chaos into a manageable investigation