The Ins and Outs of Debugging Ruby with Byebug

Share this article

The Ins and Outs of Debugging Ruby with Byebug

Ruby on Rails is, arguably, one of the finest innovations in modern web development. Its astute focus on simplicity and developer productivity, combined with user satisfaction, precipitated a culture of rapid prototyping with a heavy focus on unit testing (even if DHH has since revised his position.) Rails has, no doubt, been a catalyst for many successful startups (Twitter among them). One of the main criticisms levelled at it (particularly from people who are less experienced in using it) is the heavy use of ‘magic’. As Ruby developers, we are acutely aware that magic is simply DSLs and metaprogramming, but that can still prove frustrating to debug. Enter: Byebug.

The de-facto debugger for Rails is Byebug. It works with Rails where things like Pry fall short, comes recommended by the core Rails team, and is even bundled with Rails. This article will walk you through getting set up with basic debugging along with some slightly more advanced techniques.

Getting Set Up

Version 5.0 of Rails comes with Byebug, but if you don’t have it, you can simply add the following to your Gemfile and run bundle:

gem "byebug""

An Example

I’ve taken the liberty of knocking up a quick Rails project to debug here . It is a simple app demonstrating a broken quicksort algorithm, and we’re going to use Byebug to fix it.

Quicksort performs at O(n log n), utilising a divide-and-conquer approach to sorting an array (indeed, it is so performant that Ruby uses it internally for its array.sort method). I could wax lyrical about sorting algorithms all day, but my editor will shout at me, so we’d better get back to ByeBug:

Clone the project:

git clone https://github.com/disavowd/quicksorter.git

Run the following:

cd quicksorter && rails s

Here’s what you should see when you navigate to http://localhost:3000:

Unsorted: [77, 22, 66, 28, 39, 4, 54]

Sorted: [4, 28, 85]

And here’s the rather contrived example that has led us here:

def quicksort(array)
  return array if array.length <= 1

  pivot_index = (array.length / 2).to_i
  pivot_value = array[pivot_index]
  array.delete_at(pivot_index)

  lesser = Array.new
  greater = Array.new

  array.each do |x|
    if x <= pivot_value
      lesser << x
    else
      greater << x
    end
  end

  return quicksort(lesser) + [pivot_value] - quicksort(greater)
end

The Basics

You can enter byebug in a similar fashion to other debuggers: pick a point in the code at which to jump into the debugger and add:

...code...
byebug
...code...

or:

...code...
debugger
...code...

I’ve already done this for you in a separate controller, so if you navigate to http://localhost:3000/debug, you’ll get kicked straight into ByeBug in your terminal, which will look exactly like this:

 28:   def quicksort
 29:     @sorted = quicksort_algorithm(@unsorted)
 30:   end
 31:
 32:   def debug
 33:     byebug

=> 34: @sorted = quicksort_algorithm(@unsorted) 35: render ‘quicksort’ 36: end 37: end

Not the most intuitive interface. So what do we do now? Well, here are the byebug commands:

n (next) – executes the next line of code. next is great, but it’s not going to step into a function for you. It will simply execute the function without walking you through the code.

For the times you want to go line by line, use:

s (step into) – This will continue onto the next stack frame and jump you to the corresponding source. Speaking of source:

l (list) – This outputs the source code around the currently executing line. You can pass - to it to see the code before it. You can also pass start and end line numbers separated with a hypen: l 2-6 will show the code in the currently executing file from lines two to six.

c (continue) – This will continue the program’s execution until it either concludes or it hits another breakpoint.

pp (pretty print) – ‘Pretty Prints’ variables. Invaluable when you’re dealing with nested hashes or other slightly more complex data structures.

q (quit) – This will exit Byebug and return execution control back to the program.

It’s easy to forget with all this control over execution that we can examine exactly what’s on the stack:

self.class

Will show you that the self is currently the ApplicationController object. What methods does it have?

m  ApplicationController

m stands for method and will yield:

debug
quicksort
quicksort_algorithm

That concludes the basics. Let’s look at a more practical example relating to our problem.

Advanced Usage

Our quicksort is sorting the elements just fine – it’s just losing half of them somewhere along the way. Wouldn’t it be great if we could set a conditional breakpoint? Oh wait… We can! Let’s kick into Byebug when the pivot value is equal to the middle value in the array we see in the browser and have a poke around.

Add the following to line 25:

byebug if pivot_value == 28

Now at the byebug console, type:

greater

You should see:

[77, 66, 39, 54]

Well, those certainly look like our missing numbers! The culprit is obviously the errant - on line 25, so changing it to + will yield a functional (if slightly naive!) quicksort algorithm.

As this contrived example neatly illustrates byebug is just a regular Ruby method call, which means that it is subject to all the great things that Ruby provides when you harness its Object Model. Learning both when and which conditions to attach to your breakpoints is a tremendous boon to productive Byebug use.

Other Advanced Techniques

f (frame) – shows information about the currently executing stack frame, including both the file and line number. This also takes an integer, so you can see what will be executing in a couple of stack frames time, for example: f 2.

th (thread) – allows you to check information relating to, and to interact with threads. list, start and stop have all been helpful to me in the past. Additionally, you can pass a number to switch context to the corresponding thread.

hist (history) – will show all of your history. This (helpfully) persists between debugging sessions.

save – saves byebug history into a file (you can specify this with an argument, or let it default to .byebug_save in your home directory)

source – This is one of the best features for power users, in my opinion – if you have a bunch of Byebug commands, aliases or custom functions that you find yourself using frequently, you can source them directly into your debugging session.

irb – This will kick you into an irb session.

bt (backtrace) – This displays the backtrace.

info – This is one of the most versatile commands. Pass args, variables, instance_variables, global_variables, locals to it for more information about what is in scope. file will show you which file is currently executing, a line count, a list of the breakpoint positions, a last modified time, and an SHA1 digest. This is absolutely invaluable when you aren’t sure if your changes have propagated.

Finishing Up

This may be a solid primer for debugging with Byebug, but we’ve barely scratched the surface. We haven’t looked at breakpoints and catchpoints, traversing the program stack or displays. For more information, I recommend this excellent cheatsheet, or better yet, just digging around and playing with it. It’s fairly intuitive after the initial learning curve and you should find this a valuable addition to your developer toolbelt.

One of the great things about Byebug is that it works wonderfully in tandem with other programs. The excellent pry-byebug adds next, step, finish, continue, and break commands to pry using byebug. Additionally, one of my default gems on any project in which I’m using minitest, is the fantastic minitest-byebug. This will kick you straight into a Byebug session in the event that one of your tests fails. With a little work, you can make this play nicely with Guard, to create a formidable unit testing tool. Finally, for you Sublime users, there is sublime_debugger, which wraps Byebug in a neat little GUI.

Happy debugging!

Frequently Asked Questions (FAQs) about Debugging Ruby with Byebug

How do I install Byebug in my Ruby application?

Installing Byebug in your Ruby application is a straightforward process. First, you need to add the gem ‘byebug’ into your Gemfile. You can do this by opening your Gemfile in a text editor and adding the line gem 'byebug'. After adding this line, save and close the file. Then, run the command bundle install in your terminal. This command will install Byebug along with any other gems listed in your Gemfile. Once the installation is complete, you can start using Byebug in your Ruby application for debugging purposes.

How do I start a debugging session with Byebug?

To start a debugging session with Byebug, you need to place the word ‘byebug’ at the point in your code where you want the execution to stop. This is known as setting a breakpoint. When you run your program, it will execute normally until it reaches the breakpoint, at which point it will open a debugging session. In this session, you can inspect variables, evaluate expressions, and step through your code one line at a time.

What commands can I use in a Byebug debugging session?

Byebug provides a variety of commands that you can use during a debugging session. Some of the most commonly used commands include ‘next’, which executes the next line of code, ‘step’, which steps into the method or block in the current line of code, and ‘continue’, which resumes program execution until the next breakpoint or the end of the program. You can also use ‘var’ to list local variables, ‘info’ to get information about the current state of the program, and ‘break’ to set additional breakpoints.

How can I navigate through my code with Byebug?

Navigating through your code with Byebug is done using the ‘next’, ‘step’, and ‘continue’ commands. The ‘next’ command executes the next line of code in the current file, while the ‘step’ command steps into any methods or blocks in the current line of code. The ‘continue’ command resumes program execution until the next breakpoint or the end of the program. You can also use the ‘up’ and ‘down’ commands to move up and down the call stack.

Can I use Byebug to debug Rails applications?

Yes, you can use Byebug to debug Rails applications. In fact, Byebug is included by default in the Gemfile of new Rails applications. To use Byebug in a Rails application, you simply need to set a breakpoint by adding the word ‘byebug’ in your code where you want the execution to stop. When you run your Rails server and hit the breakpoint, a debugging session will open in your terminal.

How can I use Byebug to inspect variables?

Inspecting variables in Byebug is done using the ‘var’ command. This command lists all local variables in the current scope, along with their values. You can also use the ‘inspect’ command followed by the name of a variable to get detailed information about that variable. Additionally, you can use the ‘display’ command to automatically display the value of a variable whenever it changes.

Can I use Byebug to evaluate expressions?

Yes, you can use Byebug to evaluate expressions. Simply type the expression you want to evaluate at the Byebug prompt and press enter. Byebug will evaluate the expression in the context of the current scope and display the result.

How can I set conditional breakpoints with Byebug?

Setting conditional breakpoints with Byebug is done using the ‘break’ command followed by the condition. For example, break if @user.nil? would set a breakpoint that only triggers if the @user variable is nil. This can be very useful for debugging complex conditions that only occur under certain circumstances.

Can I use Byebug to debug tests?

Yes, you can use Byebug to debug tests. To do this, simply set a breakpoint in your test code by adding the word ‘byebug’. When you run your tests, execution will stop at the breakpoint and open a debugging session. This can be very helpful for understanding why a test is failing.

How can I exit a Byebug debugging session?

Exiting a Byebug debugging session is done by using the ‘quit’ command. This will immediately end the debugging session and resume normal program execution. If you want to remove all breakpoints and continue execution, you can use the ‘finish’ command.

David BushDavid Bush
View Author

David Bush is a Web Developer who travels the world and writes code. His favourite languages are Clojure, Ruby and Python. He enjoys learning new technologies, beer, good food and trying new things. www.david-bush.co.uk

debuggingGlennG
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week