Course 3.2Lesson 4 of 5
Finding a bug is a loop and not a guess. First reproduce it: make it fail every time, with the smallest input. Then locate it, by reading the traceback from the bottom, by printing the values inside the code, or by checking the result of each stage of a pipeline until you find the first stage that is wrong. Then explain why, in a sentence, before you change anything. Fix one thing, and test again, with the failing case and all the old tests, and keep the new test so that the bug cannot return. If it still fails, look again. The common causes are off-by-one errors, a wrong condition at an edge, a variable that is not what you think, and a changed list that you also used elsewhere. Explaining the code aloud often shows the bug.
Try the code and the simulations beside the lesson.
After this lesson you can
print, and by narrowing down,A beginner who meets a bug often guesses: change a line, run, change another, run. Sometimes it works, and you do not know why. Often it makes things worse. A good programmer follows a method, and it is the same every time.
Make the bug appear every time, with the smallest input that shows it. A bug that you cannot reproduce cannot be fixed. If the program uses chance, set a seed. If it uses a big list, try a list of two items. A small failing case is also the best thing to put in a test.
Find where the program goes wrong. You have three tools.
Read the traceback. If there is an error message, read it from the bottom: the kind of error, and the line. (This is the lesson of the course on errors.)
Print the values. If there is no error, but the answer is wrong, look inside.
Predict first. What is wrong with running_total([3, 1, 4, 1, 5])? Then run it.
The expected answer is [3, 4, 8, 9, 14]. The function gives [3, 4, 8, 9]: one item short. The print inside the loop shows the values: i takes only the values 0 to 3, and the item 5 at position 4 is never added. The loop runs to len(numbers) - 1, and it should run to len(numbers). An off-by-one error, the most common bug in the world.
Notice how the debug print helped: it showed what the loop did, and the bug was clear. Always remove debug prints when you have finished, or turn them into tests.
Narrow it down. When the code has several stages, check the result of each stage, to find the first one that is wrong.
This is bisection: if the answer is wrong at the end, and right at the start, the bug is somewhere between. Look in the middle. Is it right there? Then the bug is in the second half. Otherwise in the first. Repeat. It is the binary search idea, used on your own program.
Before you change anything, say why it fails, in one sentence: "the loop stops one item too soon, because the range ends at len - 1". If you cannot say it, you do not yet understand the bug, and a fix would be a guess. A very effective trick is the rubber duck: explain the code, line by line, aloud, to a toy (or a patient friend). Very often you find the bug while you talk.
Change one thing. If you change three things and the program works, you do not know which fixed it, and you may have added new bugs.
Run the failing case: does it pass now? Then run all the old tests: did the fix break anything? Last, keep the failing case as a new test. Now this bug can never come back without a test failing. If the case still fails, go back and look again.
The same idea works for a list of commits, a long input file, or a long list of steps: test the middle, keep the half that holds the fault.
| Cause | Where to look |
|---|---|
| off by one | range(len(x) - 1), < against <=, a slice that stops one early |
| a wrong condition at an edge | > against >=, the first and the last item |
| a variable that is not what you think | print it, and print its type: a text where you expected a number |
| a list changed by accident | two names for one list, a function that changes its argument |
| the wrong order of lines | a value used before it is changed, or after |
| a typing mistake | a name that is nearly the same as another |
When you have a bug, you will find it in this table about nine times out of ten.
Start with two quick questions. Each New numbers gives you a fresh one, so you can practise as long as you like.
Then the labs and the problems:
print, or by checking each stage.Try each one on your own first. If you are stuck, open a hint. Then check with Show the answer.
Every lesson is open to read. Enrolling also lets you:
Log in to join the discussion.
Loading…
This lesson is free to read. Enroll free to keep your place and take the tests.