Computer Science Fundamentals · Foundations

Debugging

Want it in plain words first? Jump to Eli explains — the same idea, no jargon.
On this page 9 sections
  1. In 30 seconds
  2. Why this matters
  3. The college version
  4. Eli explains
  5. Worked example
  6. Key takeaway
  7. Quick check
  8. Study tools
  9. Sources & references

In 30 seconds

is the systematic process of finding and fixing defects, called bugs, in a program. Bugs come in three flavors: syntax errors that stop the code from running, runtime errors (exceptions) that crash it mid-execution, and logic errors where the code runs happily but gives the wrong answer. Good debugging is a method, not luck: reproduce the failure, narrow down where it lives, guess a cause, test the guess, fix it, and confirm you did not break something else.

Why this matters

Every programmer spends more time reading and repairing code than writing it fresh, so debugging is the skill that decides how fast you actually ship working software. Treating it as a disciplined method, rather than random edits until the error disappears, turns a frustrating hunt into a repeatable procedure you can teach and improve. The same reproduce-isolate-hypothesize-verify loop underlies scientific experiment, medical diagnosis, and troubleshooting a car, so the habit transfers far beyond code. As systems grow and lean on tests and automated tooling, engineers who can localize a defect quickly and prove their fix did not cause a are the ones trusted with the hardest problems.

The college version

What debugging is

Debugging is the systematic process of finding and fixing defects in software. A defect, or bug, is any mismatch between what a program does and what it is supposed to do. It helps to separate two things that beginners often merge: the and the defect. The symptom is what you observe, such as a crash, a wrong number on screen, or a page that never loads. The defect is the underlying cause in the code, such as a variable initialized to the wrong value or a comparison that should have been less-than rather than less-than-or-equal. Debugging is the work of tracing from the visible symptom back to the hidden defect and then correcting it. Note that testing and debugging are related but distinct: testing is how you discover that a defect exists (and is treated as a phase in the software development life cycle), while debugging is how you locate and remove it.

The three kinds of bugs

Most defects fall into three categories, and knowing which one you are facing points you at the right tactic. A means the code is not valid in the language; the parser rejects it before the program runs at all, so nothing executes until you fix it, for example a missing colon or unbalanced parenthesis. A runtime error, called an exception in many languages, happens while the program is executing: the code was syntactically valid, but it hit an operation it could not perform, such as dividing by zero (ZeroDivisionError), using a name that was never defined (NameError), or adding a string to a number (TypeError). The program stops and reports the error type and location. A logic error, also called a semantic error, is the sneakiest: the program runs to completion and reports no error, but it does the wrong thing. The code you wrote is not the code you meant. Syntax and runtime errors announce themselves; logic errors you have to catch by checking that the output is actually correct.

A systematic method

Effective debugging is a loop, not a guess. First, reproduce the failure reliably: find the exact input or steps that trigger it, because a bug you cannot summon on demand is nearly impossible to confirm you have fixed. Second, isolate or localize it: narrow the problem to the smallest region of code that still shows it, using techniques like commenting out sections, checking values at the boundaries, or binary search over the code and inputs. Third, form a hypothesis: state a specific, falsifiable guess about the cause ("the loop stops one element early"). Fourth, test the hypothesis: add a check or run the debugger to confirm or reject it, and resist changing several things at once, since that hides which change mattered. Fifth, apply the fix. Sixth, verify: rerun the failing case to confirm it now passes, and rerun cases that used to work to make sure the fix did not introduce a regression, a new defect in previously-correct behavior. When you are stuck, it is often more productive to step back and question an assumption you have been treating as certain than to keep editing the same line.

Tools and techniques

A handful of tools cover most day-to-day debugging. The simplest is inserting print statements (or structured logging) to reveal what values variables actually hold at chosen points; logging is the durable version, since you can leave it in and switch it on when a problem appears in production. A debugger is the power tool: Python's pdb, for instance, lets you set breakpoints that pause execution at a chosen line, single-step through the source one line at a time, inspect the call stack and the values of variables in each frame, and evaluate expressions in that context. When a program crashes, it prints a stack trace (a traceback): read it from the bottom up, because the bottom names the error and the deepest call, while the lines above show the chain of calls that led there, pointing you to the file and line to inspect first. Finally, is the low-tech technique of explaining your code line by line to someone else, or even to an inanimate rubber duck; the act of articulating what each line should do often surfaces the mistaken assumption on its own.

A note on the word "bug"

The story every programmer hears is that the word "bug" was born in 1947, when operators of Harvard's Mark II computer traced a fault to a moth caught in a relay, taped the moth into the operations log, and wrote beside it "first actual case of bug being found." The page survives and is held by the Smithsonian. It is a wonderful story, but the etymology is more careful than the legend: "bug" for a technical fault was already decades old, used by Thomas Edison in the 1800s, which is precisely why the log note was a joke. Grace Hopper, who worked on the Mark II, delighted in retelling the incident and made it famous, though historians attribute the actual log entry to another team member. So the moth did not coin the term; it made a pun that stuck.

Eli, the EliExplains learning guide

Eli explains

The same idea, in plain words

Explain it like I’m 10

A bug is when a program does something you did not mean it to do. Debugging is playing detective to find out why and fix it. There are three kinds of trouble. Sometimes the computer will not even start your program because you wrote something it cannot read, like a sentence with no period. Sometimes it starts running and then stops with an error, like being asked to split a pizza among zero people. And sometimes the worst kind happens: the program runs with no complaint at all but gives a wrong answer, so you only notice if you actually check. To catch these, you first make the problem happen on purpose, then hunt for the exact spot it goes wrong, make a guess about the cause, test the guess, fix it, and check you did not break anything else.

Picture it like this

Debugging is like finding out why a recipe came out wrong. The cake being flat is the symptom; the real bug might be that you used salt instead of sugar. You do not throw out the whole recipe. You reread each step, figure out where it went off, fix that one thing, and bake again to check.

Where the picture stops working

The recipe analogy breaks down because a cake has only one outcome per bake, while a program can hide a logic bug that only appears on certain inputs and looks perfect on all the others. And a computer follows your instructions exactly and silently, so unlike a bad recipe it will never warn you that the result 'tastes wrong'; you have to check the output yourself.

Worked example

Suppose a function is supposed to return the largest number in a list. It starts a variable biggest at 0, then loops through the numbers and keeps any value larger than biggest. On the list [3, 8, 2] it correctly returns 8, so it looks fine. But a bug report says it returns 0 for [-5, -2, -9]. Step through the method. Reproduce: run it on [-5, -2, -9] and confirm it returns 0 instead of -2. Isolate: the loop and the comparison look right, so the suspect is the starting value. Hypothesize: seeding biggest at 0 assumes every number is at least 0, so when all values are negative none ever beat 0 and it returns the seed. Test: print biggest each iteration and watch it never change. Fix: seed biggest with the first element (biggest = nums[0]) and compare the rest against it. Verify: it now returns -2 for the all-negative list, and rerunning [3, 8, 2] still returns 8, so there is no regression. Both runs were checked in Python.

Key takeaway

Debugging is a repeatable method, not a lucky guess: identify whether the bug is a syntax, runtime, or logic error, then reproduce, isolate, hypothesize, test, fix, and verify no regression.

Quick check

3 questions here, of 5 in this lesson’s practice set. Answers stay hidden until you check.

Question 1 of 3foundational

Which kind of error is detected by the parser before the program runs at all?

Choose an answer, then check it.
Question 2 of 3intermediate

A program runs to completion with no error message but prints the wrong total. This is best described as:

Choose an answer, then check it.
Question 3 of 3foundational

In a debugger such as Python's pdb, what does setting a breakpoint let you do?

Choose an answer, then check it.
Practice all 5

Keep learning

Ready to build on this? Continue to the next lesson.

Practice this lesson
Study tools & related lessonsYou’ll learn to · Common mistakes · Easily confused · Key vocabulary · Related

You’ll learn to

  • Define debugging and distinguish a bug (defect) from its symptom.
  • Distinguish syntax errors, runtime errors (exceptions), and logic (semantic) errors.
  • Apply a systematic method: reproduce, isolate, hypothesize, test, fix, and verify no regression.
  • Explain common tools and techniques: print/logging, a debugger with breakpoints, reading a stack trace, and rubber-duck debugging.
  • Analyze a concrete logic bug to locate and fix its root cause.

Common mistakes

  • Changing several things at once, then not knowing which change fixed (or broke) anything.

    Change one thing at a time and re-test, so each result tells you something definite about the cause.

  • Treating a crash message as noise and ignoring the stack trace.

    Read the traceback: the bottom names the error and the failing line, and the lines above show how you got there. It usually points near the defect.

  • Assuming a program is correct because it ran without an error message.

    Logic errors produce no error at all. Check the output against what you expected, ideally on inputs designed to expose edge cases.

  • Fixing the failing case but never re-running the cases that used to work.

    Always verify against previously-working inputs to catch a regression before it reaches users.

  • Editing code randomly hoping the error disappears.

    Follow the method: reproduce, isolate, hypothesize, test, fix, verify. Random edits often mask a symptom while leaving the real defect in place.

Easily confused

Syntax error vs. Logic error

A syntax error stops the program before it runs and is flagged by the parser; a logic error lets the program run to completion with no message but yields the wrong result.

Runtime error (exception) vs. Logic error

A runtime error halts execution and announces itself with an error type and location; a logic error stays silent, so you only find it by checking that the output is correct.

Testing vs. Debugging

Testing reveals that a defect exists (a life-cycle activity); debugging is the separate work of locating that defect and removing it.

Key vocabulary

Bug (defect)
A flaw in a program that makes it behave differently from what was intended.
Debugging
The systematic process of locating a defect in code and correcting it.
Symptom
The observable sign of a bug, such as a crash or a wrong result, as distinct from the underlying cause.
Syntax error
Code that is not valid in the language; the parser rejects it before the program runs, so nothing executes until it is fixed.
Runtime error (exception)
An error raised while a syntactically valid program is executing, such as dividing by zero; it halts the program and reports an error type.
Logic error (semantic error)
A defect in which the program runs and reports no error but produces the wrong result because the code does not match the intent.
Breakpoint
A marker set in a debugger that pauses a program at a chosen line so its state can be inspected.
Stack trace (traceback)
The report a crash prints showing the error and the chain of function calls that led to it, used to locate the failing line.
Regression
A new defect introduced into behavior that previously worked, often as a side effect of a fix or change.
Rubber-duck debugging
Explaining code line by line to another person or an object, which often surfaces a wrong assumption on its own.

Sources & references

  1. Errors and Exceptions — Python Documentation
  2. pdb — The Python Debugger — Python Software Foundation
  3. Think Python, 2nd Edition — Appendix A: Debugging — Allen B. Downey / Green Tea Press
  4. September 9, 1947: First Instance of Actual Computer Bug Being Found (This Day in History) — Computer History Museum

EliExplains lessons are original prose written from the open, credible references above. See Copyright & Licensing.

Researched 2026-08-19

Educational content only. It is not medical, legal or professional advice. Found an error? Tell us.