skip to content

Rake Tasks

Rake defines project tasks in Ruby inside a Rakefile, with descriptions, dependencies, arguments and namespaces. Interviewers ask how you would automate a repeatable chore and order its steps.

on this pageshow

explore

questions

6

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%

answer

  1. a Rakefile is plain Ruby
  2. task name: %w[prereqs] do ... end
  3. each prerequisite runs once per run
  4. -T lists only described tasks
  5. bare rake runs :default

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.

solid answer

~40 s

`task :build do ... end` defines a task; `task release: %w[build tag]` adds prerequisites, which Rake invokes in order before the task's own actions, and each task runs **at most once per `rake` run** even if several tasks depend on it. `desc "Build the gem"` attaches a description to the next task defined. `rake -T` (`--tasks`) lists only tasks that have a description, showing the first sentence; `-D` prints the full text, `-AT` includes undescribed tasks, `-P` shows prerequisites and `-W` shows where each task is defined. Running `rake` with no task name runs the task named `default` (`task default: :build`); if none exists it fails with "Don't know how to build task 'default'". Rake finds the `Rakefile` by searching upward from the current directory and also loads `rakelib/*.rake`.

code

ruby · 22 lines
ruby
# Rakefile
require_relative "lib/ledger_kit/version"

GEM_FILE = "pkg/ledger_kit-#{LedgerKit::VERSION}.gem"

desc "Build the gem into pkg/"
task :build do
  mkdir_p "pkg"
  sh "gem", "build", "ledger_kit.gemspec", "-o", GEM_FILE
end

desc "Tag the release in git"
task tag: :build do
  sh "git", "tag", "v#{LedgerKit::VERSION}"
end

desc "Build, tag and push the gem"
task release: %w[build tag] do
  sh "gem", "push", GEM_FILE
end

task default: :build

go deeper

for a junior

Write a task with desc and prerequisites, list tasks with rake -T, and set a default task.

for a middle

Explain once-per-run invocation, why -T hides undescribed tasks, how repeated declarations merge, and when to use -P, -W and --dry-run.

for a senior

Design task graphs so shared steps run once, keep helper tasks undescribed, and make failures stop the run through sh.

for a principal

Decide which chores belong in Rake versus CI configuration or scripts, so local and pipeline runs share one entry point.

## A Rakefile is just Ruby Rake is Ruby's build and task runner, shipped with Ruby as a bundled gem. Its input file, the **`Rakefile`** (also `rakefile`, `Rakefile.rb` or `rakefile.rb`), has no special format: it is Ruby code that calls methods from Rake's DSL. The `rake` command looks for it in the current directory and then in parent directories, and runs tasks from the directory where it was found. Extra task files named `*.rake` in a top-level `rakelib/` directory are imported automatically. ## Defining tasks The central method is **`task`**: - `task :build do ... end` — a task named `build` with one action (the block). - `task tag: :build` or `task release: %w[build tag]` — the hash syntax lists **prerequisites**. It really is a Ruby hash: key is the task name, value the prerequisites. - `task :release do |t| ... end` — the block receives the task object, so `t.name` is available. A task may be declared several times; each declaration **adds** prerequisites and actions to the same task rather than replacing it. Inside actions you get Rake's file helpers such as `sh`, `mkdir_p`, `cp` and `rm_r`; `sh` raises and stops the run when the command exits non-zero. Use `do ... end` for task blocks, not braces: with the parenthesis-free Rakefile style, `{ }` binds to the last method call in the prerequisites expression instead of to `task`. ## How prerequisites run When you run `rake release`: 1. Rake invokes each prerequisite in order: first `build`, then `tag`. 2. `tag` has its own prerequisite `build`, which has **already run** in this process, so it is skipped. 3. Finally `release`'s own actions run. That once-per-run rule is what makes a dependency graph safe: shared prerequisites such as `build` run exactly once. A cycle (`a` needs `b`, `b` needs `a`) fails with "Circular dependency detected". ## Describing and listing tasks **`desc "..."`** applies to the **next** task defined. The listing commands read it: | Command | Shows | |---|---| | `rake -T` / `--tasks` | tasks **with** a description, first sentence only, plus argument names | | `rake -T release` | the same, filtered by a pattern | | `rake -AT` | every task, including undescribed ones | | `rake -D` / `--describe` | full descriptions | | `rake -P` / `--prereqs` | every task and its prerequisites | | `rake -W` / `--where` | the file and line where each task is defined | A task without `desc` still runs; it is just hidden from `-T`. That is often deliberate for internal helper tasks. ## The default task `rake` with no task name runs the task called **`default`**. It is an ordinary task, usually one with only prerequisites: - `task default: :build` — `rake` builds the gem. - `task default: %w[test build]` — `rake` runs the checks and then builds. If no `default` task exists, a bare `rake` fails with `Don't know how to build task 'default'` and suggests `rake --tasks`. ## Useful flags while developing - `rake -n release` (`--dry-run`) traces what would be invoked and executed without running actions. - `rake --trace release` prints every invoke and execute step and full backtraces. - `rake -f other.rake` uses a different Rakefile.

  • If both tag and release depend on build, how many times does build run during rake release?
    Once. Rake marks a task as invoked the first time it runs in a process, and later invocations in the same run return immediately. `release` invokes `build`, then `tag` asks for `build` again and gets a no-op, so the gem is built a single time.
  • Why does a task you just added not appear in rake -T?
    `rake -T` lists only tasks that have a description. Put `desc "..."` on the line before the `task` call, or use `rake -AT` (or `-P`) to see undescribed tasks too. The task itself runs either way.
  • What happens if you declare task :build twice in the same Rakefile?
    Both declarations apply to the same task: their prerequisites and actions are appended, and the actions run in the order they were defined. To replace a task rather than extend it, clear it first with `Rake::Task[:build].clear`.

saying these in an interview costs you the question

  • Rake runs a shared prerequisite once for each task that depends on it.
  • rake -T lists every task, whether or not it has a desc.
  • A Rakefile uses its own syntax, not Ruby.
  • Declaring task :build a second time replaces the first definition.
  • A bare rake with no default task silently does nothing.
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

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

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