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
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:
return array if array.length <= 1
pivot_index = (array.length / 2).to_i
pivot_value = array[pivot_index]
lesser = Array.new
greater = Array.new
array.each do |x|
if x <= pivot_value
lesser << x
greater << x
return quicksort(lesser) + [pivot_value] - quicksort(greater)
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:
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)
32: def debug
=> 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:
Will show you that the self is currently the
ApplicationController object. What methods does it have?
m stands for
method and will yield:
That concludes the basics. Let’s look at a more practical example relating to our problem.
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:
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:
th (thread) – allows you to check information relating to, and to interact with threads.
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
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.
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
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.
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.