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.
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).
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.
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.
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.
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.
# 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.
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
This tutorial covered Ruby's BEGIN and END blocks with practical examples showing initialization, cleanup, and error handling patterns.
Author
List all Ruby tutorials.