16
$\begingroup$

I'm sure we've all had this kind of conversation:

Student: Mr. Choirbean, my code is crashing.

Teacher: Okay, what error is it giving you when you crash?

Student: Error message? Was there one of those? I don't know what it said.

Teacher: (sighs) Okay, there's always an error message. Let's run it again and see...

Students who can find and fix their own bugs are empowered and more successful. So, how do you foster independence in your budding debuggers?

$\endgroup$
  • 1
    $\begingroup$ For an intro CS class, I have found the following tool helps students with the process of stepping through the code line by line: pythontutor.com/visualize.html#mode=edit . I show the TAs how to use it, then demo it in class, then make students use it in the first lab. After that, it's up to students if they want to use it or not, but they report that they do use it and it helps them. In intro, my goal is to empower them in cs, so I try to reduce time spent debugging as much as possible. $\endgroup$ – David White Jun 3 '17 at 7:11
  • 1
    $\begingroup$ You've just described all my programming classes. $\endgroup$ – rcpinto Jun 6 '17 at 3:58
  • $\begingroup$ Long ago in college while learning C, I wrote some code: int arr[10], i; for ( i = 1 ; i <= 10 ; i++ ) arr[i] = 0; - The program locked up, no error message. TA would not tell me why. 2 hours later, I had learned an important lesson. $\endgroup$ – user737 Jun 14 '17 at 18:43
  • $\begingroup$ Lead by example. During you lecturer instead of running your code examples why not step through them? $\endgroup$ – MIKE Jun 22 '17 at 21:07
13
$\begingroup$

Debugging is more a craft, an art, than a science. That's where the pedagogical challenge lies.

That said, here are a handful of tools/techniques I used to varying success:

  • Model the debugging process. I would project sample working code, break that code, and talk through the error messages (and how to read them). Trying to instill the patience and thoroughness required is harder than explaining the actual errors.
  • Give students broken code... One of the first review activities I did at the end of a unit involved having students correct broken code. Missing semi-colons, commas in for-loop declarations where semi-colons should be, misspelled function names, errant brackets. This is the opposite of the above, and students should work in both directions: break working code; fix broken code.
  • ...to correct by hand. This is where differentiation can shine. It might take reading printed code to recognize where there is a problem. Take time to fix errors off the screen in order to make them more recognizable on the screen.
  • Not all errors are created equal. Just because a program runs doesn't mean it's free from bugs. This can be the most pernicious because everything appears to be correct on the surface. The Debugging appendix of Think Java is a great resource for this issue. Having students categorize errors into types further solidifies the understanding of bugs and how to eradicate them.
  • When in doubt, print yourself a message. While it can lead to messy code as debugging messages get commented out, I'd rather a student have messy, working code with signs of thoughtful debugging than clean code that is broken or incorrect vis-a-vis its spec.

In the end, students need to build their pattern recognition, and that comes with a lot of time and a lot of focused practice. There is also an inevitable connection among debugging skill, programming environment, and language. My students were better at C than Python when it came to fixing their programs. That's due to several factors: C being less forgiving, Python feeling simpler leading to carelessness, and the IDE trying to be helpful with errors as students wrote Python. In C, it took the compilation step to find an error whereas Python would identify warnings immediately, a "feature" that muddied the debugging process.

$\endgroup$
11
$\begingroup$

I was a Girls Who Code mentor this year. The exercises provided by GWC included "debug this program" activities on a regular basis, so students got used to reading and understanding error messages. By the end of the year, when they were doing their own project, they were quite competent at debugging.

$\endgroup$
  • $\begingroup$ Are you able to share any of these activities? $\endgroup$ – Ellen Spertus May 27 '18 at 16:40
  • $\begingroup$ Sorry, I no longer have access. In general, I can tell you the error exercises usually involved an error related to what they'd just learned. $\endgroup$ – G. Ann - SonarSource Team May 28 '18 at 6:59
6
$\begingroup$

Because debugging can be learned very easily through experiance, I'd try and help the students self-correct when they make mistakes. When correcting mistakes, ask the students to type the corrections themselves, so that the corrections will be better ingrained in their minds. Also try to correct them by leading them through the debugging process. You can lead them through by asking questions, like "What line does the error message refer to?" "What is that line supposed to do?" etc. Eventually, they will start following the steps themselves, and will learn to do it without your help.

$\endgroup$
2
$\begingroup$

I prefer not to use a debugger, myself, for the simple reason that they are frustrating and don't actually run the same program (breakpoints and such can modify the logic).

There is not a lot of actual science in Computer Science, but finding out why programs go wrong is an exception to that. You have an artifact that is acting in a strange way and you want to first understand what it is doing and why. This is science - a new species of frog with novel behavior. So, use the scientific method. Form an hypothesis about what it is doing and then attempt to verify or rebut the hypothesis. You can use a debugger for this, or simple output statements. If it behaves as expected in one place look elsewhere for the error.

But, my actual practice is to try to avoid this situation as much as possible by using Test-Driven Development. This actually incorporates the science into the process from the beginning, rather than after the fact. It also guarantees, provided you use it correctly, that when an error occurs you don't have a mess requiring an autopsy, but a small glitch that you can immediately fix.

In effect, the test that you write (before you write the code to make it pass), is an hypothesis about the code. If it passes all is well. But if the test fails, you have an improper hypothesis about the actual code. As long as the test is reasonable, it must be the code that is wrong.

This is also a great way to understand an old program - a "dusty deck." What is this program written ten years ago by people no longer in the organization actually doing? How can I make it do what it needs to do now? The answer is to make hypotheses about it and capture them in tests, using a unit testing framework (junit or one of its equivalents). If the tests pass you have increased your understanding since your hypothesis was correct. If it fails, you need to think again. Once you understand it, or at least the relevant parts for the current task, you are in a position to modify it. But do this by writing new tests for the new desired behaviors.

If you program for an hour or two without tests and make an error somewhere you will probably find it difficult to debug. If you make changes to the program, hoping for the best, it will just get worse as the organization of the thing will probably worsen (entropy). But if you program three or four minutes at a time, with tests previously written to capture - in code - your understanding, you are unlikely to ever get frustrated. Unit testing speeds you up. It doesn't slow you down. Even if the number of lines of code in the tests is more than the number of lines of code in the application. Typing isn't what makes programming hard. Understanding is. Capture the understanding.

Of course you need to teach them how to use this technique through demonstration, just as with anything else. And when they come to you with a problem, have them show you the tests.

Note also that if it feels foolish to write a four line test for a three line method that it isn't that I'm not smart enough to write a short method correctly the first time. But when a program is developed incrementally, as it must be, the assumptions I make now may be invalidated later by features added later. A good testing framework doesn't just run the recent test on the recent method. It runs all the tests you have written and so the inconsistencies show up immediately. And of course, it might be either the new thing or the old thing that has to be updated. So write new tests and continue.

$\endgroup$
1
$\begingroup$

Making mistakes causes your brain to grow, see https://www.ted.com/talks/carol_dweck_the_power_of_believing_that_you_can_improve

Also explain that the error messages are there to help you, some one put in effort to get the computer to do that. They can not fix it for you (they do not know what you want it to do), but they can try to help you see what is wrong.

Often when a pupil calls me over for help. They will, as I arrive clear the error message. Some times they even close the program (thus loosing all of there work).

I think the problem stems from calling error messages “Errors”. Pupils (and often experienced programmers), then try instead of minimising errors, to minimise error messages. This is achieved, by ignoring them, deleting them, choosing techniques and languages that minimise them.

We need therefore to embrace error messages. And as my first point says embrace errors.

Let you pupils see you make mistakes (don't prepare everything up front), let them see how you deal with them.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.