Instance Methods and Class Methods

Published by Programming Line in Ruby. Last updated .

Every method you define in a Ruby class belongs to one of two worlds: it either runs on individual objects created from that class (an instance method), or it runs on the class itself, treated as an object in its own right (a class method). Understanding the difference — and why Ruby even lets you call methods "on a class" — is the key to writing well-organized object-oriented Ruby, from simple data objects to factory methods and shared utilities.

Overview: How Methods Work in Ruby

In Ruby, everything is an object — 42.class returns Integer, nil.class returns NilClass, and crucially, String.class returns Class. That last fact matters here: a class is itself an object, an instance of the built-in class Class. Once you accept that, instance methods and class methods stop being two unrelated features and become one consistent idea: every method is defined on some object, and called on that object (or its instances).

An instance method is defined with def method_name inside a class body. It becomes available on every object created with ClassName.new, and it operates on that specific object’s state — its instance variables, the ones starting with @. When you call book1.describe, Ruby searches for describe starting at book1‘s singleton class, then Book, then any modules Book includes, then Book‘s superclass, and onward up the ancestor chain until it finds a match, or reaches BasicObject and raises NoMethodError. This chain is exactly what makes mixins, inheritance, and super work; Book.ancestors will print the exact search order Ruby uses.

A class method is defined with def self.method_name (or, equivalently, def Book.method_name) inside the class body. While Ruby evaluates a class body, self refers to the class itself, so this defines a method directly on Book‘s own singleton class — a hidden, per-object class that every object in Ruby has, including classes. That is why class methods are invisible to instances (book1.total_books fails) and instance methods are invisible on the class (Book.describe fails): they live in two different objects’ method tables. Class methods are the right tool for behavior that concerns the class as a whole rather than any single instance: factory methods that build objects a particular way, counters or registries that track instances, and utility methods that need no specific object’s state.

Syntax

class ClassName
  def instance_method_name(args)
    # runs on an instance; can read/write @instance_variables
  end

  def self.class_method_name(args)
    # runs on the class itself
  end

  class << self
    def another_class_method(args)
      # grouped class methods; equivalent to def self.another_class_method
    end
  end
end
Form Defines Called via
def greet Instance method object.greet
def self.greet Class method ClassName.greet
def ClassName.greet Class method (equivalent, less common) ClassName.greet
class << self ... end A block for grouping several class methods ClassName.method_name

Examples

Example 1: An instance method and a class method that counts instances

The @@total_books class variable is shared by the class and every instance. Each call to initialize (an instance method) increments it, and the class method self.total_books reads it back.

class Book
  attr_accessor :title, :author

  @@total_books = 0

  def initialize(title, author)
    @title = title
    @author = author
    @@total_books += 1
  end

  def describe
    "#{title} by #{author}"
  end

  def self.total_books
    @@total_books
  end
end

book1 = Book.new("Dune", "Frank Herbert")
book2 = Book.new("Neuromancer", "William Gibson")

puts book1.describe
puts book2.describe
puts Book.total_books

Output:

Dune by Frank Herbert
Neuromancer by William Gibson
2

describe is an instance method: each call operates on the receiver’s own @title/@author. total_books is a class method: it is only callable on Book itself, and it reports state (@@total_books) shared across every instance, not any single book’s data.

Example 2: A class method as a factory, grouped with class << self

Factory methods are class methods that build and return an instance, often after doing some conversion the plain initialize shouldn’t have to know about.

class Temperature
  def initialize(celsius)
    @celsius = celsius
  end

  def to_fahrenheit
    @celsius * 9.0 / 5 + 32
  end

  class << self
    def from_fahrenheit(f)
      new((f - 32) * 5.0 / 9)
    end
  end
end

temp = Temperature.from_fahrenheit(98.6)
puts temp.to_fahrenheit.round(2)

Output:

98.6

Inside class << self ... end, self is Temperature‘s singleton class, so any def in that block defines a class method — identical in effect to writing def self.from_fahrenheit, just easier to read when you have several class methods together. Note that inside from_fahrenheit, calling bare new(...) works because the implicit receiver is Temperature itself.

Example 3: A registry built from class instance variables

This example tracks every Product ever created using @all, a class instance variable (a single @, owned by the Product object itself, not shared with subclasses the way @@ is).

class Product
  @all = []

  class << self
    attr_reader :all

    def register(product)
      @all << product
    end

    def most_expensive
      all.max_by(&:price)
    end
  end

  attr_reader :name, :price

  def initialize(name:, price:)
    @name = name
    @price = price
    self.class.register(self)
  end
end

Product.new(name: "Keyboard", price: 45)
Product.new(name: "Monitor", price: 220)
Product.new(name: "Mouse", price: 25)

puts Product.most_expensive.name
puts Product.all.map(&:name).join(", ")

Output:

Monitor
Keyboard, Monitor, Mouse

Every Product.new call runs the instance method initialize, which calls self.class.register(self) — an instance method reaching back out to a class method to record itself. attr_reader :all, written inside class << self, generates a class method that reads the class instance variable @all. This pattern (a registry of every instance, built from class methods and a class instance variable) is common in real Ruby code, from ORMs to plugin systems.

How It Works Step by Step

Tracing Book.new("Dune", "Frank Herbert") from Example 1 shows exactly what Ruby does:

  1. Book.new is itself a class method (inherited from Class) that allocates a blank Book object, then calls initialize on it with the given arguments.
  2. Inside initialize, self is bound to the new object, so @title and @author are set on that specific instance, while @@total_books += 1 updates the class variable shared by Book and every instance.
  3. book1.describe is called: Ruby checks book1‘s singleton class (empty), then Book, where it finds describe. It runs with self bound to book1, so title and author resolve through the attr_accessor-generated readers to book1‘s own data.
  4. Book.total_books is called: the receiver is the Book class object itself, so Ruby searches Book‘s singleton class first — where total_books lives, because it was defined with def self. — and runs it with self bound to Book, returning @@total_books.

Common Mistakes

Mistake 1: Forgetting self. when calling a setter inside a method

Inside a method, an assignment like balance = ... is always parsed as creating a local variable, never as calling a balance= setter — even if an attr_accessor generated one.

class Account
  attr_accessor :balance

  def initialize(balance)
    @balance = balance
  end

  def apply_interest(rate)
    balance = balance * (1 + rate)
  end
end

acct = Account.new(100)
acct.apply_interest(0.1)
puts acct.balance

Output:

100

The right-hand side balance correctly calls the reader, but the left-hand side balance = silently creates a brand-new local variable that shadows the setter and is discarded when the method returns. @balance is never touched. The fix is to make the receiver explicit:

class Account
  attr_accessor :balance

  def initialize(balance)
    @balance = balance
  end

  def apply_interest(rate)
    self.balance = balance * (1 + rate)
  end
end

acct = Account.new(100)
acct.apply_interest(0.1)
puts acct.balance

Output:

110.0

self.balance = ... unambiguously calls the setter method, so it actually updates @balance.

Mistake 2: Defining an instance method when you meant a class method

Leaving off self. defines a method on instances, not on the class — a very common typo when writing what was meant to be a class-level counter.

class Counter
  @@count = 0

  def increment_count
    @@count += 1
  end

  def count
    @@count
  end
end

Counter.increment_count

Output:

NoMethodError: undefined method 'increment_count' for Counter:Class

increment_count was defined with a plain def, so it only exists on instances of Counter, never on Counter itself. Calling it directly on the class raises NoMethodError. Prefixing both methods with self. fixes it:

class Counter
  @@count = 0

  def self.increment_count
    @@count += 1
  end

  def self.count
    @@count
  end
end

Counter.increment_count
Counter.increment_count
puts Counter.count

Output:

2

Best Practices

  • Use instance methods for behavior that depends on one object’s own state; use class methods for behavior that concerns the class as a whole, like factories, registries, or counters.
  • Prefer class << self ... end when a class has several class methods — it reads clearly as "everything below is class-level" instead of repeating self. on every line.
  • Reach for a class instance variable (@count, read via a class-level attr_reader) over a class variable (@@count) for most class-level state — @@ variables are shared down the entire subclass hierarchy, which can surprise you the moment a subclass appears.
  • Inside an instance method, always call setters as self.attribute = value, never bare attribute = value, to avoid accidentally creating a local variable.
  • Let attr_accessor/attr_reader/attr_writer generate simple getters and setters instead of hand-writing them.
  • Name factory-style class methods for what they build, like User.from_json or Point.origin, so the call site reads like a sentence.
  • Remember that 0, "", and [] are all truthy in Ruby — only nil and false are falsy, which matters if a method (instance or class) returns one of these as a value another piece of code checks with if.

Practice Exercises

  • Write a class Dog with an instance method bark that returns "Woof!", and a class method self.species that returns "Canis familiaris". Call Dog.species, then predict (and explain) what happens if you try Dog.new.species instead.
  • Extend the Product registry from Example 3 with a class method self.count that returns how many products exist, using all.size. Create three products and confirm Product.count prints 3.
  • Give a Circle class an instance method area (using Math::PI * radius ** 2) and a class method self.unit_circle that returns a new Circle with radius 1, following the factory pattern from the Temperature example. Confirm Circle.unit_circle.area.round(2) prints 3.14.

Summary

  • Instance methods (def name) run on objects made with .new and operate on that object’s own @instance_variables.
  • Class methods (def self.name, or several grouped inside class << self ... end) run on the class object itself.
  • A class is an object too — an instance of Class — so a class method is really just a method defined on that particular object’s singleton class.
  • Method lookup for instances walks the ancestor chain (singleton class, class, included modules, superclasses); class methods live on the class’s own singleton class.
  • Use class methods for factories, registries, and class-wide state; use instance methods for behavior tied to one object.
  • Always write self.attribute = value for setters inside instance methods, and double-check you used self. when you meant a class method — both are classic sources of bugs.