Dev tip 5/99

When I started programming the first habit I developed to determine if a long function was executing correctly was to print important values and inspect the results in my terminal. It’s crude, but it is a dependable style of debugging that works with any language. It is slow and takes a lot of effort with very large applications.

I later discovered debuggers after a few years or programming. Debuggers allow stepping through a program interactively. Instead of executing a large function or application and inspecting print statements, adding a debugger breakpoint right before the lines of code in question allows executing each line and verifying the values of variables and results in a REPL. With dynamic languages like ruby and python the breakpoints allow the full use of the language as well. You can define new functions, inspect private methods or private instance variables, make new globals, and anything else you could do with a REPL.

Ruby 2.x has the byebug gem. Python has the built in pdb package, but I prefer using ipdb. Both debuggers have different ways of invoking the debugger (command line, inside a repl, etc). My favorite way is to add a breakpoint and then execute the code. Here’s how to it in both ruby and python:

# ruby
require 'byebug'
class Hello
def say(msg)
print msg
# python
class Hello(object):
def say(msg):
import ipdb; ipdb.set_trace()
print msg

The basic commands I find most useful are:

  • s or step to step into a function
  • n or next to execute the current statement and stop at the next line or function
  • c or continue to continue executing until another breakpoint is reached
  • up and down to move up and down the stack trace
  • where to see where the line is in the stack trace
  • l or list to show the surround source code of the breakpoint
  • puts (ruby) or print (python) variables

There are other commands to familiarize with but the above is enough to see the power of a debugger. See the byebug guide and python pdb documentation for more information.