I Challenge You to Debug Those 7 lines of Code Under 9 Minutes

February 27, 2017Flavian Hautbois7 min read

thumbnail

There’s something I want you to learn. I can’t tell you what it is right now. You’ll have to trust me blindly and follow the instructions closely.

Part 1 — Be a challenger

We’re going to look at some buggy python code. Don’t leave just yet if you have never written python before! You will find something to apply to you preferred everyday language, I promise. If I lied, you are allowed to insult me abundantly through your favorite channel (here’s my Twitter, you’re welcome). Here’s your code, commented so that you know what happens.

def foo(a, methods=[]): # methods defaults to [] if not specified for i in range(3): # Append an anonymous function, which returns `x + i` # when given `x` methods.append(lambda x: x + i)

sum\_methods <span style="color: #ff7800;">\=</span> <span style="color: #3b5bb5;">0</span>
<span style="color: #8c868f;">\# For each function \`method\` of in \`methods\`, sum \`method(a)\`</span>
<span style="color: #ff7800;">for</span> method <span style="color: #ff7800;">in</span> methods:
    sum\_methods <span style="color: #ff7800;">+=</span> method(a)
<span style="color: #ff7800;">return</span> sum\_methods

You expect your function to output the following:

  • foo(0) = (0 + 0) + (0 + 1) + (0 + 2) = 3
  • foo(1) = (1 + 0) + (1 + 1) + (1 + 2) = 6
  • foo(2) = (2 + 0) + (2 + 1) + (2 + 2) = 9
  • foo(2, [lambda x: x]) = ((2)) + ((2 + 0) + (2 + 1) + (2 + 2)) = 11

Simple, right? Let’s try it in our terminal.

>>> foo(0) 6 >>> foo(1) 18 >>> foo(2) 36 >>> foo(2, [lambda x: x]) 14

Well, there seems to be a minor error. But wait, could we have missed something? Let’s check again.

>>> foo(0) 24 >>> foo(1) 45 >>> foo(2) 72

WHAT.

THE.

He’s not screaming what you think he is

But, you’re not going to get beaten down by this, right? You will pull your shirtsleeves up your arms (provided you wear a shirt, which would be unusual but we’re not judging), and you will find out what is wrong using well-placed print statements or other debugging practices you know.

Set a stopwatch, try to debug the code by clicking here, and please let me know how much time it took you to understand and fix what’s wrong in the comments, and what practices you used. K**eep both** **for** loops and don't change the **foo** function calls because it would be too easy. Got it? Go!


Part 2 — Behold the Light

Before jumping to the answer, let’s talk about debuggers. Every language I know has a way to be debugged. Debuggers lets you explore each line of the code in a convenient way.

Every debugger has at least the following commands, that you can use directly in python code:

  • Set a break point, where the execution will halt: pdb.set_trace() . I usually set my breakpoints with import pdb; pdb.set_trace() since Python is not bothered with importing the same library several times and in an inline fashion.
  • Continue until next break point: c
  • Next line: n
  • List the surrounding code: l
  • Help: h

Debuggers are Life
—  Experts

So how much quicker could you have been by using a debugger instead of trial-and-error and print statements?

I’ve tried this hypothesis on two separate groups of people: one where they used print statements, and one where they used the debugger. The second group took 9 minutes on average, and the first one 14 minutes! That’s a 35% speedup on an 8-line-long program.

Now imagine you encountered these bugs hidden in a much bigger program? I know a lot of developers that spend hours debugging a bug with print statements and waste a lot of time. The challenge is to find out exactly where you are in the code, but you get used to it very quickly, either by setting a lot of breakpoints, or by using the l command. Debuggers walk you through the code and they make understanding some new code much easier.

Mia found out about debuggers at an early age and is still amazed! Be like Mia.

Do you want your solution then? I’ve made a video of me using a python debugger (don’t mind the occasional typos please). It's a bit long but I kept it real-time so you can see the debugging process more clearly. Here’s the video:

https://www.youtube.com/watch?v=1LFDflOw4zE

Additional info: methods was broken because lists in python are mutable objects. It means that when methods is set to the default value (the empty list) and is then changed, the default value is also updated (and becomes the empty list with one more element). This is a common pitfall that you can fix using a None default value. Here is the full corrected code:

def methods_generator(i): # Scopes the i to not overwrite it return lambda x: x + i

def foo(a, methods=None): if methods is None: #Prevents the default value to be changed methods = [] for i in range(3):
methods.append(methods_generator(i))

sum\_methods <span style="color: #ff7800;">\=</span> <span style="color: #3b5bb5;">0</span>
<span style="color: #ff7800;">for</span> method <span style="color: #ff7800;">in</span> methods:
    sum\_methods <span style="color: #ff7800;">+=</span> method(a)
<span style="color: #ff7800;">return</span> sum\_methods

Part 3 — Debugging resources

I promised I would write about other languages, and I will keep this promise right now! Here are useful resources I came across, please don’t hesitate to submit more! I also including resources about code linting (that analyzes your code looking for typos and common errors) and gotchas (which are common pitfalls in a language, just like the mutable list default value above).

PHP

Javascript

Python

Java

C

Ruby


Happy debugging, happy life!

Flavian Hautbois

Flavian Hautbois

Web Developer at Theodo