What is the difference between include and extend in Ruby?

Updated: 

This article explains the difference between include and extend Module methods for sharing functionality between Ruby classes, with examples from Rails applications.

This lesson is from Full Stack Rails Mastery.

In Ruby, include and extend are two Module methods that allow you to share functionality between classes, but they do so in subtly different ways.

This can lead to confusion, especially in a Rails context where both are commonly used.

In short: Use include for instance-level behaviour and extend for class-level functionality.
This article looks at include and extend in detail, breaking down their differences and providing real-world examples from Rails applications to help you decide when to use each. 
The Basics of include and extend  
include: 
When you include a module, its methods become instance methods of the including class. This means every instance of the class gains access to the module’s methods. 

Example: 
module Greetable
  def greet
    "Hello!"
  end
end

class User
  include Greetable
end

user = User.new
puts user.greet  # Outputs "Hello!"

extend: 
When you extend a module, its methods become class methods of the extending class (or object). This means the methods are available on the class itself, not its instances. 

Example: 
module Greetable
  def greet
    "Hello!"
  end
end

class User
  extend Greetable
end

puts User.greet  # Outputs "Hello!"

Using extend on individual objects
You can also use extend by calling it on an object. In our example above, if we didn't include the Greetable module in the User class, we can still run this on a User object like this: 
module Greetable
  def greet
    "Hello!"
  end
end

user = User.new
user.extend(Greetable)

puts user.greet  # Outputs "Hello!"

When you call extend on an object (rather than a class), the methods from the module are added as singleton methods to that specific object. This allows you to modify the behaviour of individual objects dynamically, without affecting the class or other instances. 

Here: 
 1. extend adds the greet method to this specific user object
 2. Other instances of User remain unaffected: 
another_user = User.new
another_user.greet  # Raises a NoMethodError


Real-Life Use Cases in Rails 
Let’s explore common scenarios in Rails where include and extend are used effectively. 
1. Using extend: Class-Level Behaviour 
Example: Adding user-friendly ids and slugs to a Model with the FriendlyId gem
class Article < ApplicationRecord
  extend FriendlyId
  friendly_id :title, use: [:slugged, :history, :finders]
end

Here, extend is used because FriendlyId provides methods like friendly_id that operate at the class level, configuring how slugs are generated and handled for the entire model. 
Other Examples: 
 • Enumerize for defining enumerated attributes. 
 • Utility methods for querying or filtering records (Searchable module). 
 
2. Using include: Instance-Level Behaviour 
Example: Adding Background Job Functionality with Sidekiq::Job

class AddUserToMailingListJob
  include Sidekiq::Job

  def perform(user_id)
    user = User.find(user_id)
    MailingList.add(user.email)
  end
end

Here, include is used because Sidekiq::Job provides methods that operate on instances of the job class. Sidekiq creates a new instance of AddUserToMailingListJob for each job execution.  
Other Examples: 
 • Models: Adding reusable instance methods (e.g., Taggable for tags). 
 • Controllers: Sharing logic via concerns (e.g., authentication filters). 

Key Scenarios with Code Examples 
Scenario 1: Sharing Class-Level Utility Methods 
module Searchable
  def search_by_name(name)
    where("name LIKE ?", "%#{name}%")
  end
end

class Product < ApplicationRecord
  extend Searchable
end

Product.search_by_name("Gadget")  # Class-level behaviour

Here, extend makes the search_by_name method available to the Product class for querying records. 
Scenario 2: Adding Reusable Instance Methods 
module Taggable
  def tags_list
    tags.map(&:name).join(", ")
  end
end

class Post < ApplicationRecord
  include Taggable
end

post = Post.new
puts post.tags_list  # Instance-level behaviour

Here, include allows each instance of Post to access tags_list, which operates on instance-specific data. 
Scenario 3: Reusable Controller Logic 
module Authenticated
  def authenticate_user
    redirect_to login_path unless current_user
  end
end

class AdminController < ApplicationController
  include Authenticated
  before_action :authenticate_user
end

Here, include adds authentication logic as reusable instance methods for controllers.  

Another example is for pagination with the Pagy gem:
class ApplicationController < ActionController::Base
  include Pagy::Backend
end

In summary, use include for instance-level behaviour and extend for class-level functionality. By following the examples and guidelines above, you can write cleaner, more maintainable code in your Rails applications.