skip to content

In Ruby's debug gem, how do binding.break and the rdbg command start a debugging session, and what does require "debug" do?

level: middleimportance: must knowfreq 58%

answer

  1. bundled gem since Ruby 3.1
  2. binding.b and debugger are aliases
  3. require "debug" starts a nonstop session
  4. rdbg script.rb stops at line 1
  5. rdbg -c -- bundle exec rspec

basics

~20 s

The debug gem, bundled with Ruby since 3.1, defines binding.break (aliases binding.b and debugger) once require "debug" has started a session; execution stops there at an (rdbg) prompt. rdbg runs a script or, with -c, a command under the debugger without editing code.

solid answer

~40 s

`require "debug"` loads the debug gem and starts a session in **nonstop** mode (equivalent to `rdbg --nonstop --no-sigint-hook`), so the program runs normally until it reaches `binding.break`, `binding.b` or `debugger`, where it stops at an `(rdbg)` prompt showing the surrounding source and frames. Without the gem loaded, `binding.break` is undefined and raises `NoMethodError`. Alternatively, `rdbg tax_report.rb` runs a script under the debugger and **stops at its first line**, where you set breakpoints with `break`; `rdbg -c -- bundle exec rspec spec/tax_spec.rb` does the same for an executable on `PATH`, and the `--` keeps the command's own flags away from `rdbg`. Under `bundle exec`, the Gemfile must list `gem "debug"`. The gem replaced the old `lib/debug.rb` in Ruby 3.1.

code

ruby · 20 lines
ruby
require "debug"

class TaxCalculator
  RATES = {"DE" => 19, "FR" => 20}

  def total_for(order)
    net = order.sum { it[:price] * it[:qty] }
    binding.break if net > 1_000
    net + tax_on(net, order.first[:country])
  end

  private

  def tax_on(net, country)
    rate = RATES.fetch(country)
    (net * rate / 100.0).round(2)
  end
end

TaxCalculator.new.total_for([{price: 600, qty: 2, country: "UK"}])

go deeper

for a junior

Know that require "debug" plus binding.break stops the program at an (rdbg) prompt, and that binding.b and debugger mean the same.

for a middle

Explain nonstop mode versus rdbg's stop at start, rdbg -c with the -- separator, and why the gem must be in the Gemfile under Bundler.

for a senior

Choose between code breakpoints and rdbg -c for test suites and servers, and keep debugger gems and stray breakpoints out of production.

for a principal

Set a team default for debugging tooling so that local, CI and container workflows use one debugger consistently.

## The debug gem in one paragraph Ruby's current debugger is the **debug gem** (`debug`, version 1.11 with Ruby 4.0). It replaced the old, unmaintained `lib/debug.rb` in Ruby 3.1 and has been a **bundled gem** since then, so it is installed with Ruby but, like any gem, must be in the Gemfile when you run under `bundle exec`. It ships an executable, **`rdbg`**, and a library you can `require`. ## Starting from inside the code If you can edit the code, the usual flow is: 1. `require "debug"` near the start of the program (or in a test helper). 2. Put **`binding.break`** where you want to stop. **`binding.b`** and **`debugger`** are aliases of the same method. 3. Run the program as usual: `ruby tax_report.rb`. `require "debug"` does more than load code: it **starts a debug session** in nonstop mode, the same as `rdbg --nonstop --no-sigint-hook`. That means: - the program does not pause at its first line; - Ctrl-C is left to the program instead of dropping into the debugger; - each `binding.break` sets a one-shot breakpoint on the next line and stops there with an `(rdbg)` prompt, printing the source around the current line and the top frames. Two failure modes are worth recognising: | Situation | What happens | |---|---| | `binding.break` reached with no `require "debug"` and no `rdbg` | `NoMethodError`: `Binding#break` is defined only by the debug gem. | | Gem loaded (`require "debug/session"`) but no session started | `debugger` returns without stopping, because it checks for an active session first. | The first is the classic one: `binding.irb` works in any file because Ruby's prelude defines it lazily, but `binding.break` needs the gem loaded. ## Starting from the outside with rdbg If you would rather not edit code, run the program **under** the debugger: - `rdbg tax_report.rb` — like `ruby tax_report.rb`, but it **stops before the first line**. Set breakpoints (`break TaxCalculator#tax_on`), then `continue`. - `rdbg -n tax_report.rb` (`--nonstop`) — do not stop at the start; useful when breakpoints are already in the code. - `rdbg -c -- bundle exec rspec spec/tax_spec.rb` — `-c` (`--command`) means the first argument is an **executable on `PATH`** (`rake`, `rspec`, `bundle`, `rails`), not a Ruby file. The `--` separates `rdbg`'s options from the command's: `rdbg -c rake -T` would hand `-T` to `rdbg`. - `ruby -r debug/start tax_report.rb` — the library form of plain `rdbg`. While a program runs under `rdbg`, Ctrl-C suspends it into the console, which helps when it seems stuck. ## Library entry points at a glance | Require | Same as | |---|---| | `require "debug"` | `rdbg --nonstop --no-sigint-hook` | | `require "debug/start"` | `rdbg` (stop at start) | | `require "debug/open"` | `rdbg --open` (remote, stop at start) | | `require "debug/open_nonstop"` | `rdbg --open --nonstop` | ## Keeping it out of production - Put `gem "debug"` in the `:development, :test` groups only. - Remove `binding.break` lines before committing; a stop in a server or a CI job waits for console input that never comes. - Prefer `rdbg -c` for one-off sessions so nothing has to be edited and reverted.

  • Why does binding.break raise NoMethodError in a file where binding.irb works?
    Ruby's prelude defines `Binding#irb` and loads IRB on first use, so `binding.irb` works anywhere. `Binding#break` is defined only by the debug gem's session code, so it exists after `require "debug"`, `ruby -r debug`, or when the program runs under `rdbg`. Add the require (and `gem "debug"` under Bundler) or start the program with `rdbg`.
  • What is the difference between rdbg tax_calc.rb and rdbg -c -- rake tax:report?
    Without `-c`, the argument is a Ruby script run like `ruby tax_calc.rb`. With `-c`, the first argument is an executable found on `PATH`, such as `rake`, `rspec` or `bundle`, and it is launched with the debugger loaded. The `--` stops `rdbg` from parsing the command's own options.
  • Why might a program started with rdbg stop immediately even though you only wanted binding.break stops?
    Plain `rdbg` stops at the first line so you can set breakpoints before anything runs. Pass `-n` (`--nonstop`) to skip that initial stop, or type `continue` to run to the first breakpoint. `require "debug"` in the code uses nonstop mode by default.

saying these in an interview costs you the question

  • binding.break works in any Ruby 4.0 file without requiring debug.
  • require "debug" pauses the program at its first line.
  • rdbg -c expects a Ruby file path rather than an executable name.
  • The debug gem is Ruby's old lib/debug.rb under a new name.
  • Under bundle exec, require "debug" works even when the Gemfile omits the gem.