skip to content

Workflow Tools

IRB, the debug gem, Rake, RDoc and YARD, and the RBS type-checking family are the everyday tools around Ruby code. Interviewers ask how you explore, debug, automate and document a codebase.

on this pageshow

explore

questions

27

In Ruby, which comment does RDoc attach to a method, and how do you write it in RDoc markup or in Markdown?

level: juniorimportance: must knowfreq 42%

answer

  1. comment right above the definition
  2. *bold* _italic_ +code+
  3. = Heading, indented code block
  4. # :markup: markdown per file
  5. .rdoc_options or rdoc --markup

basics

~20 s

RDoc documents a class, module, method, constant or attribute with the comment block directly above its definition. That comment is RDoc markup by default (bold, italic, +code+); a # :markup: markdown line switches the file to Markdown.

solid answer

~40 s

RDoc parses the source and gives each `class`, `module`, `def`, `alias`, constant and `attr_*` the comment block that **immediately precedes** it. By default that comment is **RDoc markup**: `*bold*`, `_italic_`, `+monospace+`, `= Heading`, a code block made by indenting, and `{text}[url]` links, while names such as `GeoLookup::Client#geocode` link automatically. To write **Markdown** instead, put `# :markup: markdown` at the top of the file, set `markup: markdown` in the project's `.rdoc_options`, or run `rdoc --markup markdown`; then you use `**bold**`, backticks and fenced code. `rdoc lib` writes HTML to `doc/`. In Ruby 4.0 rdoc is a **bundled gem**, so a Rakefile that does `require "rdoc/task"` under Bundler needs rdoc in the Gemfile.

code

ruby · 13 lines
ruby
# :markup: markdown

module GeoLookup
  # Resolves a free-text address to a `GeoLookup::Place`.
  #
  # ```ruby
  # GeoLookup::Client.new(api_key: "k").geocode("Brandenburger Tor")
  # ```
  #
  # Returns **nil** when nothing matched.
  class Client
  end
end

go deeper

for a junior

Recall the positional rule: the comment directly above a def or class documents it. Know the RDoc markup basics, bold, italic, +code+, and that # :markup: markdown switches a file to Markdown.

for a middle

Explain the three places the markup format is chosen: the per-file directive, .rdoc_options and the --markup flag. Mention automatic cross-links for names like Client#geocode and where rdoc writes its output.

for a senior

Show you know the tooling around it: rdoc as a bundled gem in Ruby 4.0 and the Gemfile entry that implies, RDoc::Task in the Rakefile, and rdoc -C for a coverage report.

for a principal

Frame the format choice for a team: RDoc markup is still the default but slated to give way to Markdown, so a new gem standardising on Markdown now avoids a later migration of every comment.

## What RDoc does **RDoc** is the documentation generator that ships with Ruby. It reads `.rb` and `.c` sources plus stand-alone `.rdoc` and `.md` pages, builds a model of your classes, modules and methods, and renders it as HTML (the `rdoc` command) or as data for the `ri` terminal viewer. It does not execute your code: everything it knows comes from parsing the files and reading the comments next to the definitions. Since **Ruby 4.0** rdoc is a **bundled gem** rather than a default gem. The `rdoc` and `ri` commands are still installed with Ruby (Ruby 4.0.7 ships rdoc 7.0.4), but a program that does `require "rdoc"` or `require "rdoc/task"` while running under Bundler fails unless the Gemfile lists `rdoc`. ## Which comment documents which object The rule is positional: - The comment block that **immediately precedes** a `class`, `module`, `def`, `alias`, constant assignment or `attr_reader`/`attr_writer`/`attr_accessor` becomes that object's documentation. - A trailing comment on the same line as the definition is where **directives** such as `:nodoc:` go, not prose. - In a C extension, the comment above the C function that implements a method documents that method. - A whole `.rdoc` or `.md` file becomes a separate page that is not tied to any code object. For a geocoding gem, that means the explanation of `geocode` sits directly on top of `def geocode`, and the overview of the gem sits on top of `module GeoLookup`. ## RDoc markup versus Markdown RDoc's own format, **RDoc markup**, is the default for `.rb` and `.c` files. Markdown is supported and is the recommended format for stand-alone pages; the RDoc project states it plans to retire RDoc markup in favour of Markdown once Markdown reaches feature parity. | Element | RDoc markup | Markdown | |---|---|---| | Heading | `= Heading`, `== Sub` | `# Heading`, `## Sub` | | Bold | `*word*` | `**word**` | | Italic | `_word_` | `*word*` | | Monospace | `+word+` or `<tt>words</tt>` | `` `word` `` | | Link | `{text}[url]` | `[text](url)` | | Code block | indent the lines | fenced with backticks | | Tables | not supported | supported | In both formats RDoc **cross-links names automatically**: writing `GeoLookup::Place` or `GeoLookup::Client#geocode` in a comment produces a link when that object is documented. ## Switching a project to Markdown There are three places to choose the format, from narrowest to widest: 1. **Per file:** a `# :markup: markdown` line at the top of the Ruby file. 2. **Per project:** `markup: markdown` in a `.rdoc_options` YAML file at the project root (`rdoc --write-options` writes one from the options you pass). 3. **Per run:** `rdoc --markup markdown lib`. The accepted formats are `rdoc` (the default), `markdown`, `rd` and `tomdoc`; the last two are legacy and discouraged. ## Building and previewing - `rdoc lib` parses `lib/` and writes HTML into `doc/` by default (`--op` changes it). - `rdoc --ri` writes `ri` data instead, under your home directory, so `ri` can show your own code in the terminal. - `rdoc -C lib` prints a coverage report of undocumented items instead of generating files. - In a Rakefile, `require "rdoc/task"` and `RDoc::Task.new` define `rdoc`, `rerdoc`, `clobber_rdoc` and `rdoc:coverage` tasks. ## A worked example In RDoc markup, a geocoding client might read: ```ruby module GeoLookup # Resolves free-text addresses to coordinates. # # = Usage # # client = GeoLookup::Client.new(api_key: "...") # client.geocode("Brandenburger Tor") # # Returns a GeoLookup::Place, or +nil+ when *nothing* matched. class Client end end ``` The indented lines become a code block, `+nil+` renders as code, `*nothing*` as bold, and `GeoLookup::Place` becomes a link. The same comment in Markdown would use a fenced block, backticks and `**nothing**`, preceded by `# :markup: markdown` at the top of the file. ## Common mistakes - Writing Markdown in a file RDoc still treats as RDoc markup, so `**bold**` renders literally. - Using the shorthand on several words: `*two words*` does not render bold, because the `*`, `_` and `+` shorthands cover a single word; longer spans need `<b>`, `<i>` or `<tt>` tags. - Putting YARD `@param` tags in a project built with plain `rdoc`: RDoc renders them as ordinary text, it does not understand them as structured tags.

  • Does RDoc understand YARD tags such as @param and @return?
    No. RDoc has no tag model; `@param address [String]` is just a line of text in the comment. RDoc's own structured hooks are directives like `:call-seq:`, `:args:` and `:yields:`. If you want tags parsed into parameter tables and type lists, you run YARD over the same comments instead.
  • Why can `require "rdoc/task"` in a Rakefile fail on Ruby 4.0 when it worked on 3.4?
    Ruby 4.0 turned rdoc from a default gem into a **bundled gem**. Under `bundle exec`, Bundler only exposes gems the Gemfile resolves, so a bundled gem that is not listed raises `LoadError`. Adding `gem "rdoc"` to the Gemfile (usually in a development group) fixes it.

saying these in an interview costs you the question

  • RDoc reads docstrings from a string placed inside the method body.
  • Any comment anywhere in the class documents the next method RDoc sees.
  • RDoc markup uses **double asterisks** for bold, just like Markdown.
  • Markdown in comments needs no declaration; RDoc detects it automatically.
  • RDoc renders YARD @param tags as a structured parameter table.
  • In Ruby 4.0, rdoc is a default gem and needs no Gemfile entry.
open as a page

In Ruby, what happens when execution reaches binding.irb, and how do you resume or stop the program afterwards?

level: juniorimportance: must knowfreq 68%

basics

~20 s

binding.irb pauses the program at that line and opens an IRB prompt inside that scope, with its locals, self and instance variables live. exit (or Ctrl-D) resumes the program from that line; exit! ends the process.

open as a page

In a Rakefile, how do you define a task with a description and prerequisites, and what do rake -T and the default task do?

level: juniorimportance: must knowfreq 60%

basics

~20 s

A Rakefile is Ruby: task release: %w[build tag] do ... end runs its prerequisites first, each at most once per run. desc describes the next task, rake -T lists only described tasks, and a bare rake runs the task named default.

open as a page

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%

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.

open as a page

In a Ruby gem documented with YARD, how do you describe a public method's parameters, return value, exceptions and usage with tags?

level: middleimportance: must knowfreq 45%

basics

~20 s

YARD reads @tags from the comment above a method: @param name [Type] for each positional or keyword argument, @return [Type] for the result, @raise [ErrorClass] for exceptions and @example for indented usage code. yard doc renders them as structured HTML.

open as a page

In an IRB session, which built-in commands let you explore an unfamiliar object's methods, source code and documentation without leaving the console?

level: middleimportance: must knowfreq 52%

basics

~20 s

IRB's ls lists an object's methods, constants and variables (-g filters them); show_source prints a Ruby method's definition; show_doc looks up its RDoc; cd moves self into an object; edit opens the file in your editor; help lists them all.

open as a page

At a Ruby debug gem (rdbg) prompt, what do step, next, finish and continue do, and how do info and bt help?

level: juniorimportance: should knowfreq 48%

basics

~20 s

In the debug gem, step enters the next call, next runs to the next line of the same method, finish runs until the current method returns, and continue runs to the next breakpoint. info lists locals; bt prints the call stack.

open as a page

With Ruby's ri command, how do ri Array#sort, ri IO::readlines and ri IO.readlines differ, and why can ri know nothing about a gem?

level: juniorimportance: should knowfreq 26%

basics

~20 s

ri prints installed Ruby documentation in the terminal: Array#sort names an instance method, IO::readlines a class method, and IO.readlines matches both. A gem installed with --no-document has no ri data, so ri reports Nothing known about it.

open as a page

In IRB, what does the underscore variable _ hold, and what do __ and IRB.conf[:EVAL_HISTORY] add?

level: juniorimportance: should knowfreq 38%

basics

~10 s

In IRB, _ holds the value of the last evaluated expression, or the exception object if that input raised. __ is the numbered history of results, and exists only after setting IRB.conf[:EVAL_HISTORY] or conf.eval_history.

open as a page

In Ruby's debug gem, how do the break and catch commands set breakpoints without editing code, including conditional ones?

level: middleimportance: should knowfreq 36%

basics

~20 s

In the debug gem, break sets a breakpoint on a line or a method (break tax_calc.rb:15, break TaxCalculator#tax_on) and accepts if: for a condition; catch KeyError stops when that exception is raised, even if it is rescued later.

open as a page

In Ruby, how do binding.pry, pry-byebug and byebug relate to the debug gem, and which would you pick for a new Ruby 4.0 project?

level: middleimportance: should knowfreq 42%

basics

~20 s

binding.pry opens Pry, a richer console with no stepping; pry-byebug adds byebug's stepping to it; byebug is a standalone C-extension debugger. For a new Ruby 4.0 project the default is the debug gem, bundled with Ruby, with stepping, remote attach and editor integration.

open as a page

In a Ruby gem, how do RDoc's :nodoc: and :doc: directives and YARD's @api private tag control which methods appear in the documentation?

level: middleimportance: should knowfreq 30%

basics

~20 s

RDoc documents public and protected methods by default; a trailing # :nodoc: hides a definition and # :doc: forces one in, such as a private method. YARD ignores those directives and uses @api private or @private with --hide-api private or --no-private.

open as a page

Inside an IRB session opened by binding.irb, what does IRB's debug command do, and when does it refuse to start?

level: middleimportance: should knowfreq 30%

basics

~20 s

IRB's debug command hands the paused binding.irb session to the debug gem: the prompt becomes irb:rdbg and commands like next and step work beside IRB's own. It refuses outside binding.irb, in multi-irb mode, or without a loadable debug gem.

open as a page

In Rake, how do file, directory and rule tasks decide whether to rebuild, and why does a file task depending on a plain task always rebuild?

level: middleimportance: should knowfreq 28%

basics

~20 s

A Rake file task runs only if its file is missing or older than a prerequisite's timestamp. A plain task's timestamp is always the current time, so a file task depending on one always rebuilds; rake/phony's phony task avoids that.

open as a page

In Rake, what is the difference between Rake::Task#invoke, #execute and #reenable, and why does a second invoke in one run do nothing?

level: middleimportance: should knowfreq 32%

basics

~20 s

Rake::Task#invoke runs prerequisites and then the actions, but only on the first call in a run; later calls return immediately. #execute runs only the actions, every time, skipping prerequisites. #reenable resets the flag so invoke runs again.

open as a page

How do you pass arguments to a Rake task, as in rake bump[1.4.0], and how does that differ from NAME=value on the rake command line?

level: middleimportance: should knowfreq 45%

basics

~20 s

Declare argument names after the task name, task :bump, [:version] do |t, args|, and pass values as rake bump[1.4.0]; they arrive as Strings, missing ones as nil. rake bump VERSION=1.4.0 instead sets ENV["VERSION"] for the whole run.

open as a page

In Ruby's RBS signature language, how do you declare a class's methods, including optional, keyword, nilable and block parameters?

level: middleimportance: should knowfreq 28%

basics

~20 s

RBS declares Ruby types in separate .rbs files, usually under sig/: def name: (Integer, ?String, key: Symbol) -> String. A leading ? marks an optional parameter, a trailing ? a nilable type, and { (T) -> void } a block.

open as a page

How do you set up Steep to type-check a Ruby library against its RBS signatures, and what does steep check report?

level: middleimportance: should knowfreq 22%

basics

~20 s

steep init writes a Steepfile; a target lists the Ruby to check and the sig directory. rbs collection install supplies gem signatures, and steep check reports mismatches such as Ruby::NoMethod on a value that may be nil.

open as a page

A Ruby service runs in a container without a TTY; how do you debug it with the debug gem's rdbg --open and rdbg -A, and what are the risks?

level: seniorimportance: should knowfreq 24%

basics

~20 s

Start the program as a debuggee with rdbg --open (or require "debug/open"), then connect a console with rdbg -A. It listens on a UNIX socket by default or TCP with --port; the channel is unencrypted and grants code execution, so keep it local.

open as a page

In Ruby 4.0's IRB, which rc files run at startup, in what order, and why is an .irbrc inside a cloned repository a risk?

level: seniorimportance: should knowfreq 22%

basics

~20 s

IRB loads every rc file it finds: the IRBRC path, the XDG config file, ~/.irbrc or ~/.config/irb/irbrc, then .irbrc in the current directory. Each is plain Ruby, so a repository's .irbrc runs arbitrary code; irb -f skips them.

open as a page

When does Rake's multitask speed up a build, and what races can running prerequisites in parallel introduce?

level: seniorimportance: should knowfreq 20%

basics

~20 s

Rake's multitask runs a task's immediate prerequisites in parallel on a thread pool sized by -j. It helps when they shell out or wait on I/O; shared prerequisites still run once, but Ruby data they share needs synchronisation.

open as a page

For typing a Ruby billing library, how does a Sorbet sig block differ from an RBS signature or an inline # @rbs comment?

level: seniorimportance: should knowfreq 25%

basics

~20 s

A Sorbet sig is Ruby code, sig { params(cents: Integer).returns(Receipt) }, checked statically by srb tc and, with sorbet-runtime, on real calls. RBS lives in .rbs files or experimental # @rbs comments, has no runtime cost and is checked by Steep.

open as a page

In Ruby 3.4 and later, how does IRB's type-based completion differ from its regexp completor, and why can it fall back under Bundler?

level: middleimportance: nice to knowfreq 16%

basics

~20 s

Since Ruby 3.4 IRB defaults to IRB::TypeCompletor, which uses the repl_type_completor gem and RBS to infer receiver types, so chained calls and block parameters complete. If that gem cannot load, as under a Gemfile that omits it, IRB quietly uses IRB::RegexpCompletor.

open as a page

When adding RBS signatures to an existing Ruby library, how do rbs prototype rb and TypeProf differ in the signatures they generate?

level: middleimportance: nice to knowfreq 15%

basics

~20 s

rbs prototype rb reads only the syntax, so it emits every class, method and instance variable with mostly untyped types. TypeProf abstractly runs the code and infers real types from the calls it sees; both outputs are drafts to edit.

open as a page

For a Ruby gem's CI, how would you use yard stats --list-undoc and yard doc --fail-on-warning to stop the public API documentation from rotting?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

yard stats --list-undoc prints a % documented figure and lists each undocumented public object with its file and line; it does not fail on gaps by itself. yard doc --fail-on-warning exits non-zero on warnings such as an unknown @param name.

open as a page

How would you package reusable gem build and release tasks with Rake::TaskLib, and how do Rake namespaces keep their names from clashing?

level: seniorimportance: nice to knowfreq 18%

basics

~10 s

Subclass Rake::TaskLib, which includes Rake::DSL, collect options in initialize (yield self), then define the tasks inside namespace :gem so they become gem:build and gem:release. Each Rakefile adds the whole set with one ReleaseTasks.new call.

open as a page

On a large Ruby billing codebase, how would you roll out RBS and Steep incrementally so type checking pays off without stalling feature work?

level: principalimportance: nice to knowfreq 14%

basics

~20 s

Start with the public API and money-handling paths, seed signatures with rbs prototype and TypeProf, check them with lenient Steepfile targets that tighten over time, validate signatures against the test suite with RBS_TEST_TARGET, and track progress with steep stats.

open as a page