Why Ruby Modules Matter (and How They Differ from Interfaces)

Why Ruby Modules Matter (and How They Differ from Interfaces)
Dala horses look each other

Before Ruby, I had dabbled with Java and C. To be honest, my work with those languages didn’t really go beyond the scope of university assignments. Later on, in my professional career, I had the chance to spend about a year working with C#. By that time, it had been quite a while since I last touched Java, so I’m sure a lot of things had changed. Still, I could remember some of the core concepts here and there.

I think I started writing C# after about 7–8 years of Ruby development. That adventure didn’t last too long, but I have to say I really enjoyed writing C#. I guess what I liked most was that some things felt a little less abstracted compared to Ruby. Of course, writing Ruby has always been fun for me too, but at that time some concepts didn’t really click in my head. Later, when I returned to Ruby, I often found myself asking: “Wait, why don’t we have this? Or if we don’t, then what do we have instead?”

One of those concepts I want to talk about is Modules, which are often compared across different languages when discussing Ruby. Let me be clear: this won’t be a “Module tutorial.” Instead, I’ll just try to share some personal thoughts about how they differ from similar ideas in other languages, and what exactly the concept of modules means for us Rubyists.

Class vs. Module: Identity vs. Behavior

Imagine you’re modeling the world in Ruby. You start with a simple hierarchy:

class Dog < Animal; end
# A Dog is an Animal

That makes sense—every dog is, by nature, an animal. This is identity: inheritance (<) tells us what something is.

But dogs are more than just animals. Some can swim, some can guide the blind, some can even skateboard (at least on YouTube 🐶🛹). These are capabilities, not their core identity.

That’s where modules come in:

module Swimmable
  def swim; "splish splash"; end
end

class Dog
  include Swimmable
end

Dog.new.swim # => "splish splash"
# A Dog is not a Swimmable, but it can swim

So:

  • Subclassing: “A Dog is an Animal.”
  • Modules: “A Dog can Swim.”

The distinction may seem small, but it’s the difference between defining essence and adding abilities. Classes capture what a thing is, while modules let you say what a thing can do.

Why Modules Exist in Ruby

  • Single inheritance limitation
    A class can only inherit from one superclass. Modules let you compose multiple behaviors freely.
  • Code reuse
    Common methods can be shared across different classes.
  • Namespaces
    Avoid name collisions:
module Payments; class Invoice; end; end
module Shipping; class Invoice; end; end
  • Separation of identity and behavior
    • Classes define identity (what an object is).
    • Modules add capabilities (what an object can do).
  • Mixin philosophy instead of multiple inheritance
    Ruby avoids the complexity of multiple inheritance but still allows flexible behavior composition through mixins.

Rails in Action

  • User < ApplicationRecord → User is an ActiveRecord model (identity).
  • include ActiveModel::Validations → User has validation behavior (capability).

Without modules, Rails wouldn’t be nearly as flexible—you’d end up with absurd class hierarchies like ValidatableAuthenticatableTimestampedUser.

Comparing Ruby Modules with Other OOP Languages

As I said, I don’t have much experience with languages outside of Ruby. However, with some research, I believe I can make comparisons and point out some of the differences. If I’ve explained something incorrectly, please let me know in the comments so I can fix it.

Java: Interfaces (especially since Java 8 with default methods) behave similarly to modules, but earlier versions couldn’t hold method bodies.

.NET / C#: Interfaces also evolved; since C# 8.0, default implementations make them closer to Ruby modules.

Go: Interfaces define contracts only, no shared behavior. A struct must implement everything explicitly.

In short: A Ruby module is like an interface + mixin hybrid.

Cross-Language Example: “Swimmable”

Let’s imagine we want to model a Dog that can swim.

Ruby (module mixin):

module Swimmable
  def swim
    "splish splash"
  end
end

class Dog
  include Swimmable
end

Dog.new.swim  # => "splish splash"

Java (interface with default method):

interface Swimmable {
    default void swim() {
        System.out.println("splish splash");
    }
}

class Dog implements Swimmable {}

public class Main {
    public static void main(String[] args) {
        new Dog().swim(); // prints "splish splash"
    }
}

C# (interface with default implementation):

public interface ISwimmable {
    void Swim() => Console.WriteLine("splish splash");
}

public class Dog : ISwimmable { }

class Program {
    static void Main() {
        var dog = new Dog();
        dog.Swim(); // prints "splish splash"
    }
}

Go (interface only, no method body):

package main

import "fmt"

type Swimmable interface {
    Swim()
}

type Dog struct{}

func (d Dog) Swim() {
    fmt.Println("splish splash")
}

func main() {
    var s Swimmable = Dog{}
    s.Swim() // prints "splish splash"
}

What We See

  • Ruby: module = interface + behavior
  • Java & C#: Interfaces slowly evolved to mimic Ruby modules (with default methods).
  • Go: Interfaces remain pure contracts, leaving all behavior to the implementor.

Ruby solved this decades ago with modules—giving developers mixins that are simple, flexible, and expressive.

Takeaway

Ruby’s module is not just syntactic sugar—it’s a deliberate tool to:

  • Break past single inheritance.
  • Add reusable behaviors to multiple classes.
  • Separate what something is from what something can do.
  • Keep systems flexible and composable.

Where other languages rely on interfaces, abstract classes, or traits, Ruby gives us modules: simple, powerful, and mixin-friendly.

So why did I choose this topic and go into such detail? I mentioned it briefly in the introduction, but if you’ve read this far, here’s what I really want to say: in Ruby, the concept of a module can be especially confusing for people coming from other languages. And for those moving from Ruby to another language, it can also feel tricky. Taking some time to understand what a module is, how it works, and seeing it through simple real-life examples can be really helpful.

Has anything started to take shape in your mind? Are there other Ruby concepts that still puzzle you?