Modules and Mixins

· 5 min read · Updated March 7, 2026 · beginner
modules mixins ruby-fundamentals

Modules in Ruby serve two important purposes: they act as namespaces to organize your code, and they provide a way to share behavior between classes through mixins. In this tutorial, you’ll learn both patterns with practical examples.

Why Use Modules?

Before we dive into the syntax, let’s understand why modules matter. In Ruby, a class can only inherit from one parent. But what if you want to share functionality across unrelated classes? That’s where modules come in.

Modules let you:

  • Group related classes and methods together
  • Create namespaces to avoid name collisions
  • Share behavior between classes without inheritance

Modules as Namespaces

A module can contain constants, methods, and even other classes. Grouping related code together keeps your codebase organized:

module MathUtils
  class Calculator
    def add(a, b)
      a + b
    end
    
    def multiply(a, b)
      a * b
    end
  end
  
  PI = 3.14159
  
  def self.circle_area(radius)
    PI * radius * radius
  end
end

calc = MathUtils::Calculator.new
puts calc.add(2, 3)        # => 5
puts MathUtils.circle_area(5)  # => 78.53975

The :: operator accesses constants and classes inside a module. This prevents naming conflicts—your Calculator class won’t clash with Calculator from another library.

Module Constants

Modules are great for storing constants that belong together logically:

module HTTPStatus
  OK = 200
  NOT_FOUND = 404
  SERVER_ERROR = 500
  
  def self.message(code)
    messages = { OK => "OK", NOT_FOUND => "Not Found" }
    messages[code] || "Unknown"
  end
end

puts HTTPStatus::OK           # => 200
puts HTTPStatus.message(404)  # => Not Found

Constants in modules follow the convention of being UPPERCASE with underscores.

Mixins: Sharing Behavior Across Classes

Ruby doesn’t support multiple inheritance, but mixins let you achieve similar results. Use include to add instance methods:

module Debuggable
  def debug_info
    "#{self.class} - #{instance_variables.map { |v| "#{v}=#{instance_variable_get(v)}" }.join(', ')}"
  end
end

class User
  include Debuggable
  
  def initialize(name, email)
    @name = name
    @email = email
  end
end

user = User.new("Alice", "alice@example.com")
puts user.debug_info
# => User - @name=Alice, @email=alice@example.com

Now every User instance has the debug_info method. You can include multiple modules in a single class:

module Timestampable
  def created_at
    @created_at ||= Time.now
  end
  
  def updated_at
    @updated_at ||= Time.now
  end
end

class Order
  include Debuggable
  include Timestampable
  
  def initialize(item)
    @item = item
  end
end

order = Order.new("Widget")
puts order.debug_info
# => Order - @item=Widget

Extending with Class Methods

Use extend to add module methods as class methods:

module Configurable
  def configure(&block)
    @config ||= {}
    block.call(@config) if block_given?
    @config
  end
  
  def config
    @config || {}
  end
end

class API
  extend Configurable
end

API.configure do |c|
  c[:base_url] = "https://api.example.com"
  c[:timeout] = 30
end

puts API.config  # => {:base_url=>"https://api.example.com", :timeout=>30}

The extend keyword makes the module’s methods available as class methods on the extended class.

Combining Include and Extend

You can use both in the same module to provide both instance and class methods:

module Factory
  def create(*args)
    new(*args)
  end
  
  module ClassMethods
    def brand_name
      @brand_name ||= "Generic"
    end
    
    def brand_name=(name)
      @brand_name = name
    end
  end
  
  def self.included(base)
    base.extend(ClassMethods)
  end
end

class Widget
  include Factory
end

widget = Widget.create("Button")
puts Widget.brand_name  # => Generic

The self.included callback runs when the module is included. This is a common pattern in Ruby frameworks like Rails—ActiveRecord uses this to add both instance methods and class methods through modules.

The Prepend Method

Ruby also provides prepend, which works like include but places the module’s methods earlier in the method lookup chain:

module UppercaseName
  def name
    ">> #{super} <<"
  end
end

class Person
  prepend UppercaseName
  
  def name
    "Alice"
  end
end

puts Person.new.name  # => >> Alice <<

With prepend, the module’s method overrides the class’s method, but you can still call super to access the original.

Real-World Example: Serialization

Here’s how you might use modules in a real application:

module Serializable
  def to_json
    hash = {}
    instance_variables.each do |var|
      hash[var.to_s.delete('@')] = instance_variable_get(var)
    end
    hash.to_json
  end
end

class User
  include Serializable
  
  def initialize(name, role)
    @name = name
    @role = role
  end
end

user = User.new("Bob", "admin")
puts user.to_json
# => {"name":"Bob","role":"admin"}

When to Use Mixins

Mixins work well for:

  • Shared behavior across unrelated classes (like Debuggable)
  • Adding utility methods (like Timestampable)
  • Implementing interfaces (Ruby uses duck typing, so no formal interfaces)
  • Adding cross-cutting concerns (like logging, caching)

Avoid overusing mixins—if you find yourself including many modules with unrelated functionality, consider inheritance or composition instead.

When Not to Use Mixins

Don’t use mixins when:

  • You need shared state (modules can’t store instance state)
  • You need multiple inheritance (Ruby classes still inherit from one superclass)
  • The behavior is specific to one class (keep it private)
  • You find yourself overriding methods with conditionals

Module Methods: The Three Types

Ruby modules can define three kinds of methods:

  1. Instance methods: Added via include, available on class instances
  2. Class methods: Added via extend or defined as def self.method_name
  3. Module methods: Called directly on the module with ModuleName.method_name
module Helper
  # Instance method - available when included
  def helper_method
    "instance"
  end
  
  # Class method - available when extended
  module ClassMethods
    def class_method
      "class"
    end
  end
  
  # Module method - called as Helper.module_method
  def self.module_method
    "module"
  end
  
  def self.included(base)
    base.extend(ClassMethods)
  end
end

Summary

Modules in Ruby provide two powerful features:

  1. Namespaces organize related classes and constants
  2. Mixins share behavior through include (instance methods) and extend (class methods)

Use modules to keep your code modular and DRY. But remember: composition (objects containing other objects) is sometimes clearer than inheritance or mixins. When used appropriately, modules are one of Ruby’s most powerful features for code organization and reuse.