ZetCode

Ruby BEGIN and END Keywords

last modified October 18, 2026

This tutorial explains how to use Ruby's BEGIN and END keywords. These special blocks control code execution at program start and exit.

The BEGIN keyword defines a block that runs before any other code. The END keyword defines a block that runs when the program exits. These uppercase keywords are different from the lowercase begin ... end block used for exception handling.

These hooks are useful for initialization and cleanup tasks. END blocks run on normal termination, on exit, and after an uncaught exception. They do not run after exit!, exec, or a fatal signal. Multiple BEGIN blocks run in definition order, while multiple END blocks run in reverse order.

Basic BEGIN and END Example

This simple example demonstrates the execution order of BEGIN and END blocks. Notice they run outside the normal program flow.

basic_begin_end.rb
BEGIN {
  puts "This runs first"
}

END {
  puts "This runs last"
}

puts "This runs in between"
# Output:
# This runs first
# This runs in between
# This runs last

The BEGIN block executes before any other code, while END runs after everything. The main program output appears between them as expected.

Multiple BEGIN/END Blocks

Ruby allows multiple BEGIN and END blocks. BEGIN blocks run in definition order, while END blocks execute in reverse order (like a stack).

multiple_blocks.rb
BEGIN {
  puts "BEGIN 1"
}

BEGIN {
  puts "BEGIN 2"
}

END {
  puts "END 1"
}

END {
  puts "END 2"
}

puts "Main program"
# Output:
# BEGIN 1
# BEGIN 2
# Main program
# END 2
# END 1

Notice BEGIN blocks execute top-to-bottom, while END blocks run bottom-to-top. This behavior is consistent across Ruby versions.

BEGIN with Variable Initialization

BEGIN blocks can initialize variables used in the main program. This example sets up configuration before the main code runs.

initialization.rb
BEGIN {
  $app_name = "My Ruby Program"
  $start_time = Time.now
}

puts "Welcome to #{$app_name}"  # prints "Welcome to My Ruby Program"
puts "Started at #{$start_time}"

END {
  puts "Program ran for #{Time.now - $start_time} seconds"
}

Global variables are visible inside methods and END blocks. Local variables assigned in a BEGIN block are visible at the top level, but not inside methods, so globals are safer for values shared across the whole program. The END block calculates total runtime using the BEGIN-initialized time.

Error Handling in END Blocks

END blocks execute even when the program exits early or raises exceptions. This makes them ideal for cleanup tasks.

error_handling.rb
BEGIN {
  puts "Starting risky operation"
}

END {
  puts "Cleanup always happens"
}

fail_now = true
raise "Something went wrong!" if fail_now

puts "This might not run"

With fail_now set to true, the exception terminates the main program, but the END block still runs. The final puts is never reached. Set fail_now to false to see the normal path, where the cleanup still runs.

The END block is skipped by exit!, exec, and fatal signals, so it is not a substitute for all forms of cleanup.

File Operations with BEGIN/END

A common use case is file handling - opening in BEGIN and closing in END. This keeps the file open for the whole run and closes it when the program terminates normally or with an uncaught exception.

file_operations.rb
BEGIN {
  $log_file = File.open("output.log", "w")
  $log_file.puts "Log started: #{Time.now}"
}

def log(message)
  $log_file.puts message
end

log("Processing started")

END {
  $log_file.puts "Log ended: #{Time.now}"
  $log_file.close
  puts "Log file closed"  # prints "Log file closed"
}

log("Processing completed")

The script creates output.log in the working directory. The file opens before any logging occurs and closes after all operations finish. This pattern ensures the file is closed even if the main program raises an exception.

BEGIN with Command-line Arguments

BEGIN blocks can process ARGV before the main program. This example handles early argument parsing.

argv_processing.rb
# Run: ruby argv_processing.rb --verbose

BEGIN {
  $verbose = ARGV.delete("--verbose")
  puts "Verbose mode enabled" if $verbose
}

puts "Remaining arguments: #{ARGV.inspect}"

END {
  puts "Verbose logging complete" if $verbose
}
# Output:
# Verbose mode enabled
# Remaining arguments: []
# Verbose logging complete

The BEGIN block modifies ARGV before the main program sees it. The END block can report on verbose operations if enabled. Run the script without --verbose to see the non-verbose path.

Complex Program Structure

This example shows BEGIN/END in a larger program with multiple components. The blocks help organize setup and teardown.

program_structure.rb
BEGIN {
  require 'json'
  $config = { "api_endpoint" => "https://example.com" }
}

BEGIN {
  $stats = {
    start: Time.now,
    requests: 0
  }
}

def make_request(url)
  $stats[:requests] += 1
  "response from #{url}"
end

puts make_request($config["api_endpoint"])  # prints "response from https://example.com"

END {
  duration = Time.now - $stats[:start]
  puts "Made #{$stats[:requests]} requests in #{duration.round(6)} seconds"
  File.write("stats.json", JSON.dump($stats))
}

The BEGIN blocks load dependencies and initialize tracking. The END block prints the statistics and saves them to stats.json when the program terminates normally or with an uncaught exception. The request is simulated, so the example runs without network access.

Source

Ruby Keywords Documentation

This tutorial covered Ruby's BEGIN and END blocks with practical examples showing initialization, cleanup, and error handling patterns.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Ruby tutorials.