Methods in Ruby
· 9 min read · Updated March 26, 2026 · beginner
ruby methods fundamentals
What Are Methods?
Methods are the fundamental unit of reusable code in Ruby. They let you package a sequence of operations under a name, then call that name whenever you need those operations performed. Without methods, you’d copy and paste the same code everywhere — a maintenance nightmare that Ruby spares you from.
Think of a method as a named action your program can perform on demand. “Greet the user,” “calculate a total,” “validate an email address” — each of these is a perfect candidate for a method.
Defining a Method
You define a method with the def keyword, followed by the method name, an optional parameter list, and closed with end:
def greet(name)
puts "Hello, #{name}!"
end
greet("Alice")
# => Hello, Alice!
```ruby
Method names in Ruby follow a convention: lowercase letters with underscores (snake_case). Ruby does not enforce this, but it is the community standard and reading code that deviates from it will surprise other developers.
You can also define methods that take no parameters:
```ruby
def hello
"Hello, world!"
end
hello
# => "Hello, world!"
```ruby
Ruby allows method names to end with `?` (predicates), `!` (bang methods), or `=` (setter methods). These are syntactic conventions that signal intent to other developers — the language itself treats them as part of the name.
## Parameters and Default Values
When you need a method to accept input, you define parameters — variables that receive values when the method is called:
```ruby
def power(base, exponent)
base ** exponent
end
power(2, 3)
# => 8
```ruby
Default values make parameters optional. Ruby evaluates default values left-to-right at the point of the method call:
```ruby
def greet(name = "World")
"Hello, #{name}!"
end
greet
# => "Hello, World!"
greet("Alice")
# => "Hello, Alice!"
```ruby
Be careful with mutable default values like arrays or hashes. If you write `def add_item(item, list = [])`, all calls that skip the `list` argument share the same array object. This is a classic Ruby gotcha:
```ruby
def append(item, list = [])
list << item
list
end
append("a") << "b" # this mutates the shared default array
append("c") # => ["a", "b", "c"] — surprising!
```ruby
The safer approach is to use `nil` as the default and initialize inside the method:
```ruby
def append(item, list = nil)
list ||= []
list << item
list
end
append("a")
# => ["a"]
append("b")
# => ["b"]
```ruby
## Variable-Length Arguments
Ruby lets you collect multiple arguments into a single parameter using the splat operator `*`. The method receives an array containing all the extra arguments:
```ruby
def sum(*numbers)
numbers.inject(0, :+)
end
sum(1, 2, 3)
# => 6
sum(1, 2, 3, 4, 5)
# => 15
sum
# => 0
```ruby
For keyword arguments, the double splat `**` collects them into a hash:
```ruby
def configure(**options)
options.each { |key, value| puts "#{key}: #{value}" }
end
configure(host: "localhost", port: 3000)
# => host: localhost
# => port: 3000
```ruby
Ruby 3 introduced a trailing comma feature that lets you add new parameters without touching existing call sites:
```ruby
def greet(name:, age:,)
"#{name} is #{age}"
end
```ruby
## Returning Values
Every Ruby method returns a value — the result of the last expression evaluated in the method body. You do not need an explicit `return` statement in most cases:
```ruby
def add(a, b)
a + b
end
add(2, 3)
# => 5
```ruby
The explicit `return` keyword is useful when you want to exit a method early:
```ruby
def find_first_even(numbers)
numbers.each do |n|
return n if n.even?
end
nil
end
find_first_even([1, 3, 5, 6, 7])
# => 6
```ruby
If you use `return` without a value, the method returns `nil`.
One thing to watch: setter methods (methods ending in `=`) always return the argument that was passed, not the assigned value:
```ruby
def val=(x)
@x = x
end
result = (self.val = 42)
result
# => 42 — not the value of @x
```ruby
## Predicate Methods
Methods whose names end with `?` are called predicates. They are a naming convention signaling that the method returns a boolean value:
```ruby
def empty?(string)
string.length == 0
end
empty?("")
# => true
empty?("hi")
# => false
```ruby
Ruby has many built-in predicates:
```ruby
[1, 2, 3].empty?
# => false
"hello".include?("lo")
# => true
Hash.new.respond_to?(:keys)
# => true
```ruby
The `?` suffix is a convention only. Ruby does not enforce that predicates return `true` or `false`, but idiomatic Ruby predicates always do. If a predicate can return `nil` or some other truthy/falsy value, callers may get unexpected behavior.
## Bang Methods
Methods ending with `!` are called bang methods. They signal a "dangerous" or "mutating" variant — typically one that changes the receiver in place rather than returning a new object:
```ruby
original = "hello"
loud = original.upcase!
original
# => "HELLO" — original was mutated
loud
# => "HELLO" — returns the mutated string
```ruby
Compare with the non-bang version:
```ruby
original = "hello"
loud = original.upcase
original
# => "hello" — unchanged
loud
# => "HELLO" — new string
```ruby
The `!` is a naming convention only. Ruby does not treat bang methods specially — you can define a bang method that does anything. The convention exists so developers can tell at a glance which version of a method mutates state and which one does not.
If a method has only a bang variant (like `exit!`), the bang typically indicates it terminates the process without running finalizers, as opposed to `exit` which does run them.
Do not rely on the bang to communicate danger — name your methods so the intent is clear from the name itself.
## Setter Methods
Setter methods allow assignment syntax. You define one with `def name=(value)`:
```ruby
class Person
def name=(value)
@name = value
end
end
person = Person.new
person.name = "Alice"
```ruby
Ruby provides `attr_writer` as a shortcut for single-attribute writers:
```ruby
class Person
attr_writer :name
end
person = Person.new
person.name = "Alice"
```ruby
The `=` in a setter method name requires a space between `def` and the name:
```ruby
def val=(x) # correct
@x = x
end
def val = x # wrong — Ruby parses this differently
@x = x
end
```ruby
## Method Visibility
Ruby gives you control over where methods can be called from. There are three visibility levels:
### Public
Public methods are callable from anywhere. This is the default in Ruby — if you do not specify a visibility modifier, your method is public:
```ruby
class Calculator
def add(a, b)
a + b
end
end
Calculator.new.add(1, 2)
# => 3
```ruby
### Private
Private methods cannot be called with an explicit receiver. They are only callable on `self`, which in instance method context means the current instance:
```ruby
class Greeter
def greet
"#{format_message}!"
end
private
def format_message
"Hello"
end
end
g = Greeter.new
g.greet
# => "Hello!"
g.format_message
# => NoMethodError (private method called with explicit receiver)
```ruby
Inside the instance, you call `format_message` without any receiver — Ruby implicitly calls it on `self`.
### Protected
Protected methods are callable by `self` and any instance of the same class or its subclasses. This is useful when a method needs to compare two instances without being publicly accessible:
```ruby
class Employee
attr_reader :employee_id
def initialize(id)
@employee_id = id
end
protected
def same_company?(other)
self.employee_id == other.employee_id
end
end
class Manager < Employee
def compare(e1, e2)
e1.same_company?(e2) # calling protected method on another instance
end
end
m = Manager.new(1)
alice = Employee.new(1)
bob = Employee.new(2)
m.compare(alice, bob)
# => false
```ruby
### Changing Visibility
Visibility applies to all methods defined after it, until another modifier changes it:
```ruby
class Example
def public_method; end
private
def private_method_a; end
def private_method_b; end
public
def another_public_method; end
end
```ruby
For class methods, use `public_class_method` and `private_class_method`.
## self — The Current Object
`self` refers to the current object — the receiver of the method call currently executing:
```ruby
class Dog
attr_accessor :name
def bark
puts "#{name} says woof!"
end
def introduce
puts "This is #{self.name}."
end
end
d = Dog.new
d.name = "Rex"
d.bark
# => Rex says woof!
d.introduce
# => This is Rex.
```ruby
Inside instance methods, `self` is the instance. Inside class methods, `self` is the class itself:
```ruby
class Counter
@@count = 0
def self.increment
@@count += 1
end
def self.current
@@count
end
end
Counter.increment
Counter.current
# => 1
```ruby
When you call a method without an explicit receiver inside an instance method, Ruby implicitly calls it on `self`. This is why `name` and `self.name` are equivalent inside an instance method when `name` is an accessor.
## Keyword Arguments
Keyword arguments bind by name rather than position, which makes callsites easier to read:
```ruby
def connect(host:, port: 80, ssl: false)
"Connecting to #{host}:#{port} (ssl=#{ssl})"
end
connect(host: "example.com")
# => "Connecting to example.com:80 (ssl=false)"
connect(host: "example.com", port: 443, ssl: true)
# => "Connecting to example.com:443 (ssl=true)"
```ruby
Ruby 2.1 introduced required keyword arguments using the trailing colon syntax with no default:
```ruby
def create_user(name:, email:, role: "guest")
{ name: name, email: email, role: role }
end
create_user(name: "Alice", email: "alice@example.com")
# => { name: "Alice", email: "alice@example.com", role: "guest" }
```ruby
Ruby 3 made a breaking change to keyword argument handling. In Ruby 2, a positional hash would implicitly fill keyword parameters. In Ruby 3, positional and keyword arguments are strictly separated:
```ruby
def greet(name:, greeting: "Hello")
"#{greeting}, #{name}!"
end
# Ruby 2: greet({ name: "Alice" }) worked
# Ruby 3: this raises ArgumentError
# You must call it with:
greet(name: "Alice")
# or explicitly unpack:
greet(**{ name: "Alice" })
```ruby
The double splat `**` in a method definition collects keyword arguments into a hash. The double splat at a call site unpacks a hash into keyword arguments. These are two sides of the same mechanism.
## Summary
Methods are where Ruby programs live. You now know how to define them with `def`, accept input through parameters and keyword arguments, return values explicitly or implicitly, and control visibility with `public`, `private`, and `protected`. You have seen the conventions around predicate methods (`?`), bang methods (`!`), and setter methods (`=`), and you understand how `self` refers to the current object.
The conventions Ruby uses for method naming are not enforced by the language — they are a shared vocabulary the community settled on. Following them makes your code immediately familiar to other Ruby developers.
## See Also
- [Ruby Blocks and Iterators](/tutorials/ruby-blocks-and-iterators/) — blocks are the natural companion to methods, letting you pass reusable chunks of code around
- [Ruby Classes and Objects](/tutorials/ruby-classes-and-objects/) — methods live inside classes, and understanding how objects work deepens your appreciation of method dispatch