172 Debugging [1d]

Updated . Posted . Visible to the public.

When your code does not behave as expected, debugging tools help you find the exact line where the actual behavior diverges from what you expected. This lesson covers breakpoints and logs in Ruby and JavaScript, the browser's network and DOM inspectors, and how to get unstuck when a bug resists you.

Important

Work on this lesson in teacher mode.

A debugger statement ("breakpoint") opens a REPL Show archive.org snapshot ("console") at that line, where you can inspect the program's state and move the control flow ahead manually. Since we use open source for everything, we can always find that line. When you ask a colleague for help, they will be able to help you better when you have already located the issue.

Learning goals

  • You can explain your debugging strategy: find the exact line where the actual behavior diverges from what you expected, and narrow down toward it instead of guessing.
  • You can get unstuck on your own, and ask a good question when you can't โ€” describing the actual problem rather than your attempted solution.
  • You can set a breakpoint in Ruby code, inspect the program's state, step through the code and resume, using the debugging gem the project uses (e.g. debug, byebug or pry).
  • You can debug code inside a gem by reading and temporarily changing its source.
  • You can use logs to locate a problem, e.g. by following the Rails log while you reproduce it, and add log output of your own.
  • You can set a breakpoint in JavaScript and inspect state and step through code in the browser's developer tools.
  • You can observe requests and responses in the browser's network tab, and inspect the live DOM.
  • You can debug a failing end-to-end test by pausing it and inspecting the page and the server log.
  • You review your own changes before committing, so debugging statements never end up in a commit.

Resources

Read what's new to you, skim what's familiar, skip what you already master. Stop when you can meet the learning goals.

Your agent can also generate an overview, a tutorial or an explanation for anything here, tailored to what you already know. Just ask.

Getting unstuck

Debugging tools

Debugging in Ruby

Note

Running your server as part of a single combined terminal with the bin/dev command makes Ruby debugging harder. To work around this, you should start your web server in a separate terminal as described above. If you are using terminator, bin/term is the easiest way to to achieve this.

  • Rails apps come with Ruby's debug gem in the Gemfile. This provides debugging for your app.
  • At any line, insert the command debugger (or binding.break).
  • When you start your app and the Ruby interpreter passes this line, this will open a debugging console. Do this.
    • Note that when your app is a web app, the console running your rails server will pause the server and show the debugging console. Your browser will "hang" while that debugging console is open.
    • Note that the debugger console is not a full irb console, even though it somewhat behaves that way.
  • Use the debug README Show archive.org snapshot , the debugger's help command and our Debug Ruby code card to learn to do the following in the debugging console:
    • Understand your current position in the program
    • Read local variables
    • Call methods that are reachable from the current scope
    • Switch into a full irb console and back to the debugger
    • Move the program position one statement ahead
    • Jump into the next method call
    • Jump out of the current method call
    • Exit debugging console and resume program
  • Use a simple raise statement e.g. in controller code and open the corresponding page with your browser
  • If you need to debug code that is part of a gem like rails, just (temporarily) add a puts or debugger statement to its source files. Ruby is an interpreted language after all!
  • You should be in a habit of reviewing your code before committing. Never commit a debugging breakpoint.
  • If you are debugging rspec tests, you can use rspec -b to print the full stack trace.
  • If you encounter problems caused by ActiveRecord and its database mapping, you can run ActiveRecord's query methods in the rails console and it will log the exact database interaction with related sql commands and its result.
  • Before you know where to debug, you will often find yourself staring at logs beforehand - they are your friend at this mission!
    • Use Rails.logger.info() to add your own custom messages to log/<environment>.log
    • Use tail -f log/development.log for a live view of the local Rails server's log
    • ..or tail -f log/test.log to see the same for RSpec tests!

Different debugging libraries

Ruby has always had multiple gems for debugging. When you're working on a customer project you need to check the Gemfile to see which one is used.

All Ruby debugging gems have the same basic functionality:

  • Add a breakpoint
  • Get an IRB-like console when the breakpoint is reached

A debugging gem will also offer commands to step through your program, but their names differ from gem to gem.

Each gem also uses a different expression to set a breakpoint:

Gem Breakpoint
pry Show archive.org snapshot binding.pry
byebug Show archive.org snapshot byebug
debug Show archive.org snapshot binding.break

Debugging in JavaScript

  • At any line, insert the command debugger
  • When a modern browser's JavaScript interpreter passes this line, this will open a debugging console if developer tools are already open. Do this.
    • Note that since JavaScript is single-threaded, you cannot interact with your app's frontend while the debugging console is open.
  • Use the Google DevTools debugging guide Show archive.org snapshot to learn to do the following in the browser's debugging console:
    • Understand your current position in the program
    • Read local variables or this. Observe how Chrome will automatically show the state of local variables in the Sources tab.
    • Call functions that are reachable from the current scope
    • Move the program position one statement ahead
    • Jump into the next method call
    • Jump out of the current method call
    • Exit debugging console and resume program
    • Skim over DevTools card to get a rough overview about its many capabilities
  • You should be in a habit of reviewing your code before committing (e.g. by using git add -p). Never commit a debugging breakpoint.

Debugging network traffic

  • Your browser DevTools have a Network tab. You can use it to observe outgoing network requests and their responses.
  • You can also use the curl command in your terminal to print the response of a request.

Debugging DOM changes

  • In your browser you can right-click any element and choose Inspect.
  • This will open a tree displaying the current state of the DOM. The tree is live, so it will be updated automatically when your DOM changes.
  • Do this on the large hero photo at https://makandra.com/.
    • Can you find the element for the photo?
    • Observe how the DOM changes as the photos are rotated every few seconds.

Exercises

1. Break into MovieDB

  • Put a debugger statement into a controller action of your MovieDB and open the page in your browser.
  • In the debugging console: inspect local variables and params, step over and into a few statements, switch into an irb console and back, then resume.
  • Replace the breakpoint with a plain raise "boom" and explore the error page you get in the browser.

2. Debug inside a gem

  • Pick a Rails method your controller calls (e.g. redirect_to) and open its installed source (bundle open, or ask your agent for the source_location).
  • Temporarily add a puts or debugger there, trigger it, watch it fire โ€” then revert your change. Ruby is an interpreted language: the gem source on your disk is the code that runs.

3. Step through your movie counter

  • Put a debugger statement into the movie-counter component you wrote in Working with the DOM and open the page with DevTools open.
  • Step through the handler, read the local variables and this in the Sources panel, then resume.

4. Watch the wire

  • Open the Network tab (check Preserve log) on an app like cards.
  • Switch between a few cards. What requests and responses do you see?
  • Update a card and find your form data ("params") in the request entry.
  • Throttle the network in DevTools to simulate a slow connection and reload.
  • Repeat one of the requests with curl in your terminal and compare the response.

5. Debug a failing feature spec

  • Break an expectation in one of your MovieDB feature specs so it fails.
  • Now work the toolbox: run it with rspec -b for the full backtrace; put a debugger into the spec right before the failure and inspect page in the console; re-run with NO_HEADLESS=1 and watch the browser do its thing; follow log/test.log in a second terminal while the spec runs.
  • Fix the expectation again.

6. Leave no trace

Before you commit anything from this card: check git diff and remove every debugger, puts and raise you planted. Debugging statements never belong in a commit.

Profile picture of Henning Koch
Henning Koch
Last edit
Michael Leimstรคdtner
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra Curriculum (2018-12-12 10:21)