Skip to main content

include vs extend in Ruby: Clear Rules, Real Patterns

Created: April 24, 2026 CalmOps 2 min read

The Core Rule

In Ruby modules:

  1. include adds module methods as instance methods.
  2. extend adds module methods as singleton methods to a specific object.

For classes, that usually means:

  1. include affects instances of the class.
  2. extend affects class-level methods.

Example 1: include

module Greeter
  def greet
    "hello"
  end
end

class User
  include Greeter
end

u = User.new
puts u.greet # => "hello"

greet becomes an instance method of User objects.

Example 2: extend

module Slugger
  def slug(value)
    value.downcase.gsub(" ", "-")
  end
end

class Article
  extend Slugger
end

puts Article.slug("Hello Ruby") # => "hello-ruby"

slug is available on the class itself.

Method Lookup and Ancestors

When you include a module in a class, Ruby inserts that module into the ancestor chain.

class User
  include Greeter
end

puts User.ancestors.inspect
# [User, Greeter, Object, Kernel, BasicObject]

This explains why module methods are found during instance method lookup.

self Methods in Modules

Methods defined as def self.foo are singleton methods on the module object itself.

They are not mixed in by include.

module Naming
  def self.nickname
    "mod-nick"
  end

  def instance_name
    "instance"
  end
end

After include Naming, only instance_name is injected.

Common Pattern: Class Methods + Instance Methods

Use included hook to extend class methods automatically.

module Trackable
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def track(name)
      @tracked ||= []
      @tracked << name
    end
  end

  def tracked?
    true
  end
end

This pattern is used heavily in Rails-style DSLs.

prepend in Modern Ruby

prepend inserts module before class in lookup chain.

Use it to wrap/override behavior safely:

module Logging
  def save
    puts "before"
    super
  end
end

class Model
  prepend Logging
  def save
    puts "save"
  end
end

Output:

  1. before
  2. save

Typical Mistakes

  1. Expecting include to create class methods.
  2. Defining everything with self. in module and then including it.
  3. Calling extend inside initialize without clear reason.
  4. Overusing metaprogramming for simple composition problems.

Practical Decision Guide

Use:

  1. include for shared instance behavior.
  2. extend for shared class-level behavior.
  3. prepend for method decoration/interception.

Keep module responsibilities small and explicit.

Conclusion

include and extend are simple once you map them to method lookup:

  1. include = instance side.
  2. extend = object singleton side.

Understanding this model helps you read Rails internals, build cleaner DSLs, and avoid subtle metaprogramming bugs.

Resources

Comments

Share this article

Scan to read on mobile