In Ruby's debug gem, how do binding.break and the rdbg command start a debugging session, and what does require "debug" do?
answer
- bundled gem since Ruby 3.1
- binding.b and debugger are aliases
- require "debug" starts a nonstop session
- rdbg script.rb stops at line 1
- rdbg -c -- bundle exec rspec
basics
~20 sThe 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 linesrequire "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
Know that require "debug" plus binding.break stops the program at an (rdbg) prompt, and that binding.b and debugger mean the same.
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.
Choose between code breakpoints and rdbg -c for test suites and servers, and keep debugger gems and stray breakpoints out of production.
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.