00:00:00.080
Welcome everyone! It’s time to learn some tricks in this lightning talk. My name is Zhi Ren Guoy, and I will be discussing how to add Byebug to the professional debugger toolset. I’m a software engineer at Fave, where I typically work with Ruby on Rails. I am an agilist at heart, always keen on exploring ways to work better by adopting a lean and agile mindset. Additionally, I tinker with operations and infrastructure, focusing on improving continuous integration and deployment. Today, we will have a 15-minute crash course on getting started with the Byebug debugger.
00:00:21.680
Before I start, I’d like to give a quick shout out to everyone involved in organizing Euruko 2021. Thank you for all the time, effort, and work that went into this event. Here’s a general reminder for everyone to follow the code of conduct. A little about myself: I’m currently a Ruby on Rails engineer who makes terrible memes and bad puns—I’ll share a few throughout this talk. I also co-host a monthly podcast about software development. Most of the time, I appear on social media as 'ghost eat human', a pun on my name—don't worry, I don't actually eat people.
I assume many of us, in our day-to-day jobs, ship working bugs—oh, sorry, I meant features! As part of our work, we write tests to ensure our code functions correctly. However, what do we do when we find something wrong with the program and can’t quite pinpoint the issue? Usually, we read through the code line by line, examining the logic, hoping to decipher why it isn’t working. Maybe we can figure out the issue just by reading the code.
If reading the code alone doesn’t resolve the issue, we often resort to using 'puts' statements to track the code execution. You know how it is: puts here, puts there, puts everywhere! Hopefully, we can find out where the bug occurs. However, what if puts isn’t enough? Another approach is to use a debugger, which is an application that allows us to run the program under controlled conditions.
00:03:02.800
Let’s take a look at a terrible example. In this example, we have a function that takes a user ID and a hash called 'referred'. If it’s the first time a user gets referred, and they are not in the hash, they get a discount. However, if they have been referred before and are already in the referred hash, they don’t get a discount. Naming things can be difficult, and I had a hard time coming up with a good name for this example. This code is intentionally flawed for a reason. At the end of this program, I send a message for user ID 1 and an empty hash, expecting to get a discount. However, when I run it, it returns zero.
Now, let's examine the professional Byebug way. To see how it features a certain line of code, we use puts to help mark the execution path. Since we didn’t see any similar puts statements in the output, we know that the code did not pass through that line.
Buying byebug is a Ruby debugger gem that supports MRI 2.5 and above; it does not support other implementations such as JRuby or Truffle Ruby. We can run Byebug from the command line to debug a Ruby program without editing the code. Running Byebug on the program displays the file content, and the left playback shows the number of lines along with which line the debugger is currently on. At the bottom, there is a prompt where we can input commands.
The first thing we need to know is what commands we can input. What better way to find out than calling the help command? The help command provides a list of all available commands. Rest assured, we will quickly go through some of them. By the way, quitting Byebug is much easier than quitting Vim!
00:05:06.160
Next, let’s discuss the 'list' command. Using the list command shows all the lines of source code, along with the line of code you’re currently pointing at. In this case, it’s line 1. Now, let’s talk about the 'break' and 'continue' commands. The break command sets breakpoints in the source code, and Byebug will stop execution when it reaches a breakpoint. The continue command resumes the program execution until it hits the next breakpoint or reaches the end of the program.
Based on the code logic, the program should stop at line 5. We want to stop at line 5, so we need to set a breakpoint there. The continue command will then execute until it reaches the next breakpoint or the end of the program. However, we see that execution returned 0 at line 3 instead of stopping at the breakpoint at line 5. The next command is like the continue command but proceeds to the next line instead of the next breakpoint or the end of the program. We know that the program returns at line 3, and we can confirm this by using the next command to perform a line-by-line execution.
Byebug also supports conditional breakpoints. We can set breakpoints based on specific conditions. For example, I can create a breakpoint at line 3 if a user has been referred before. Let's say we want to know the values of some variables. As professional Byebug users, we can print the values of variables using puts. In Byebug, we can type the variable name in the console, and it will show the value of the variable at the line being executed. At line 3, we can check that user ID has a value of 1.
Furthermore, using Byebug allows us to mutate variables on the fly. Here’s another example program that prints the value of 'a'. Initially, 'a' is set to 1. We can use the next command to move the code execution to the next line and then set 'a' to 42. As a result, at the end of the program, 'a' will have the value of 42.
00:07:06.960
We can also utilize Byebug to understand the scope of variables. This capability helps us determine which variables are global, instance, or local. For example, there are scenarios where we want to track when the value of a variable changes. In the case of a professional debugger, we might have puts statements all over the place to track variable values. Byebug provides another option: we can use the 'display' command to show the value of variables every time the debugger stops. The 'set' command modifies Byebug settings, and the 'set trace' command enables line execution tracing. With these two commands, we can monitor the value of a variable at every line of code execution.
Now we’ve found out where the issue is! The truth is, we all write programs that depend on gems, and this is where I feel we hit the limits of being a professional puts debugger. For instance, if we’re using a third-party gem, like a QR code generator, there’s no way to debug it using puts unless we have access to the source code itself. Even if we do, it may be a hassle to clone the repository and scatter puts all over the code. In such cases, we can use Byebug’s step command to delve into gem methods. By doing so, we can step into the gem’s code and see what’s going wrong. However, if we realize we’re lost in a rabbit hole of code, we can simply ask Byebug where we are, and it will indicate our place in the execution path.
Lastly, when using Byebug in Rails, know that the Byebug gem is included in Rails by default since version 4.2. In the code, call the 'byebug' method to initiate debugging, and the debugger will start at the line following the byebug method call.
That concludes my talk. Here are all the references I used to prepare for this presentation. I hope you all learned something today. Thank you!