Ruby Constants
last modified October 18, 2026
Constants in Ruby are variables whose names begin with an uppercase letter. They hold values that are meant to stay fixed during program execution. This guide covers defining constants, how Ruby looks them up through lexical scope and namespaces, freezing values to prevent changes, and inspecting constants dynamically with reflection methods.
Defining Constants
A constant name must begin with an uppercase letter; by convention, constant
names are written in all uppercase with underscores separating words. Ruby
allows constants to be assigned values of any type. Their values can be read
anywhere the constant is visible, and the defined? keyword reports
whether a name is a constant.
# Constants start with an uppercase letter
MAX_USERS = 100
APP_NAME = "Inventory"
PI_APPROX = 3.14159
puts "App: #{APP_NAME}"
puts "Max users: #{MAX_USERS}"
puts "Pi: #{PI_APPROX}"
puts MAX_USERS.class
puts defined?(MAX_USERS)
puts defined?(MISSING).inspect
The names MAX_USERS, APP_NAME, and
PI_APPROX are constants holding an integer, a string, and a float.
defined?(MAX_USERS) returns "constant", while
defined?(MISSING) returns nil for a name that was never
defined.
$ ./define.rb App: Inventory Max users: 100 Pi: 3.14159 Integer constant nil
Reassignment Warning
Ruby does not forbid reassigning a constant, but it prints a warning to standard error when a constant that already has a value is assigned again. The program keeps running, and the new value replaces the old one. The warning is a signal that the code is probably wrong, not an error that stops execution.
LIMIT = 10
puts "Before: #{LIMIT}"
LIMIT = 20
puts "After: #{LIMIT}"
The second assignment to LIMIT triggers a warning that includes the
line of the new and the previous definition. Running with 2>&1
captures the warning, which is written to standard error, together with the
normal output.
$ ./reassign.rb reassign.rb:3: warning: already initialized constant LIMIT reassign.rb:1: warning: previous definition of LIMIT was here Before: 10 After: 20
Constant Scope and Lookup
Ruby resolves a constant name first through the lexical scope: the module and
class bodies that textually enclose the code. If it is not found there, Ruby
checks the ancestors of the enclosing class and finally the top-level
(Object) namespace. A constant can be read explicitly with
Module::NAME, and ::NAME refers to a top-level constant
even when a nearer constant with the same name shadows it.
GREETING = "Hello from top level"
module Outer
GREETING = "Hello from Outer"
class Greeter
def say
GREETING # lexical lookup: finds Outer::GREETING
end
end
module Inner
def self.say
GREETING
end
end
end
class Other
def say
GREETING # not nested in Outer, finds top-level constant
end
end
puts Outer::Greeter.new.say
puts Outer::Inner.say
puts Other.new.say
puts Outer::GREETING
puts ::GREETING
puts Outer.const_get(:GREETING)
Inside Outer, both Greeter and Inner find
Outer::GREETING because they are lexically nested within
Outer. The class Other is not nested there, so the
lookup reaches the top-level constant. Outer::GREETING reads the
constant through its namespace, and ::GREETING forces the lookup to
the top level.
$ ./scope.rb Hello from Outer Hello from Outer Hello from top level Hello from Outer Hello from top level Hello from Outer
Constants in Classes and Modules
Constants defined in a class or module belong to that namespace and are read
with the :: operator from outside. Inside the class body and its
methods, they can be used directly. Modules are a common place to group related
configuration values, and constants lists the names defined in a
given namespace.
module Config
VERSION = "1.2.0"
DEFAULTS = { timeout: 30, retries: 3 }.freeze
end
class Server
PORT = 8080
ROLES = %w[admin user].freeze
def info
"Port #{PORT}, roles #{ROLES.join(', ')}, version #{Config::VERSION}"
end
end
puts Server::PORT
puts Server.new.info
puts Config::DEFAULTS[:timeout]
puts Server.constants.sort.inspect
puts Config.constants.sort.inspect
Server defines PORT and ROLES, which its
instance method uses directly. The method also reads Config::VERSION
through the module's namespace. The constants method returns the
names defined directly in each namespace as symbols.
$ ./classes.rb 8080 Port 8080, roles admin, user, version 1.2.0 30 [:PORT, :ROLES] [:DEFAULTS, :VERSION]
Freezing Constants
Assigning a constant only prevents rebinding its name, not changing the object it
refers to. Calling freeze makes an object immutable. Attempting to
modify a frozen object raises FrozenError. The freeze is shallow:
it applies only to the object itself, so nested strings or arrays remain mutable
unless they are frozen too.
COLORS = %w[red green blue].freeze
puts COLORS.frozen?
begin
COLORS << "yellow"
rescue FrozenError => e
puts "FrozenError: #{e.message}"
end
# freeze is shallow: the array cannot change, but the string inside it can
NAMES = ["alice", "bob"].freeze
NAMES[0] << " smith"
puts NAMES.inspect
# Freezing the hash and the string it holds
SETTINGS = { host: "localhost".freeze }.freeze
begin
SETTINGS[:host] << "!"
rescue FrozenError => e
puts "FrozenError: #{e.message}"
end
puts "string frozen? #{SETTINGS[:host].frozen?}"
Appending to the frozen COLORS array raises a
FrozenError that names the frozen class and its contents. The
NAMES array is frozen, but its first string is not, so it can still
be changed. SETTINGS freezes both the hash and its string value,
which makes the string immutable too.
$ ./freeze.rb true FrozenError: can't modify frozen Array: ["red", "green", "blue"] ["alice smith", "bob"] FrozenError: can't modify frozen String: "localhost" string frozen? true
Dynamic Constant Access
Ruby provides reflection methods for constants. const_get reads a
constant by name, const_set defines one at runtime, and
const_defined? checks whether a name exists. Calling
Object.const_defined? checks the top-level namespace. A missing
constant raises NameError.
module Limits
MIN = 1
MAX = 99
end
puts Limits.constants.sort.inspect
puts Limits.const_get(:MAX)
Limits.const_set(:STEP, 5)
puts Limits::STEP
puts Limits.const_defined?(:MIN)
puts Object.const_defined?(:Limits)
puts Object.const_defined?(:Nope)
puts Object.const_get("Limits::MIN")
begin
Limits.const_get(:Missing)
rescue NameError => e
puts "NameError: #{e.message}"
end
const_set adds STEP to Limits at runtime,
after which it appears in constants and can be read normally.
Object.const_defined?(:Limits) is true because modules
are top-level constants, while Object.const_defined?(:Nope) is
false. const_get also accepts a string with
:: separators for nested lookup.
$ ./dynamic.rb [:MAX, :MIN] 99 5 true true false 1 NameError: uninitialized constant Limits::Missing
Best Practices
Use uppercase names with underscores for constants and group related values in a
module or class. Freeze collections and strings that should not change, and
remember that freeze is shallow. Avoid reassigning constants; if a
value must change, use a regular variable or an attribute instead. Prefer
explicit Module::NAME references when a constant comes from another
namespace, so the source of the value is clear.
Source References
Learn more from these resources: Ruby Assignment Documentation, Module Documentation, and FrozenError Documentation.
List all Ruby tutorials.
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.