-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
119: Common Ruby Interview Questions on Modules vs Classes
A few years ago, I was interviewing a developer who had a stellar resume and deep experience in Java. We got to the Ruby portion of the technical screen, and I asked them to implement a simple set of utility methods for formatting currency and dates. They immediately jumped into creating a Formatter class. When I asked why they chose a class, they said, "Well, I need to put the methods somewhere." Then, they tried to call these methods by instantiating the class: Formatter.new.format_currency(100). I could tell they were fighting the language; they were trying to force Ruby to act like a strictly object-oriented language where everything must be an instance of a class. It was a classic mistake, and it's exactly the kind of thing a picky interviewer will pounce on to see if you actually "get" Ruby or if you're just translating from another language in your head.
The "Can I New This?" Litmus Test
When an interviewer asks you the difference between a class and a module, the most fundamental answer is instantiation. You can create an object from a class using .new; you cannot do that with a module. If you try to call Module.new, Ruby will throw a NoMethodError. This sounds simple, but the "why" is where the real conversation happens. Classes are blueprints for objects that hold state. Modules, on the other hand, are designed for grouping behavior or namespacing.
If you find yourself creating a class that never holds any unique instance data—meaning you're just using it as a bucket for methods—you should probably be using a module. I've seen way too many codebases cluttered with "Helper" classes that are actually just modules in disguise. It's a smell. In Ruby, if it doesn't need to be an "object" with its own identity, it doesn't belong in a class.
Include, Extend, and the Ancestor Chain
This is where most candidates start to sweat. The interviewer will likely ask: "What's the difference between include and extend?" Here is the mental model I use: include adds the module's methods as instance methods, while extend adds them as class methods. But to really impress them, you need to talk about the ancestor chain.
module Loggable
def log(message)
puts "[LOG]: #{message}"
end
end
class NetworkRequest
include Loggable
end
# The ancestor chain: NetworkRequest -> Loggable -> Object -> Kernel -> BasicObject
puts NetworkRequest.ancestors.inspect
When you include a module, Ruby inserts that module into the inheritance hierarchy immediately above the class. This is how Ruby solves the "multiple inheritance" problem. You can't inherit from five different classes, but you can include five different modules. If you use extend, you're essentially adding those methods to the singleton class of the object, bypassing the standard instance lookup. I usually tell my juniors: use include when the object is something (like Loggable), and extend when you want to give the class itself some specialized capabilities.
Namespacing vs. Mixins
Finally, be prepared to discuss the dual nature of modules. We use them as "Mixins" (as shown above) to share behavior across unrelated classes. But we also use them for namespacing to prevent collisions in large projects. This is a distinct use case. When you wrap a class inside a module—like PaymentGateway::StripeProcessor—you aren't using the module to share methods; you're using it to organize your code.
An interviewer might ask: "Can a module have a constant?" Yes, it can. In fact, that's a primary way we handle configuration. Just remember that while a class can be both a namespace and a blueprint, a module is the "purest" way to organize your code without the overhead of object instantiation. If you can explain that distinction clearly, you've already passed the most difficult part of the Ruby architectural conversation.
📋 Practical Task
Implementing a Multi-Model Taggable System
In this exercise, you will simulate a real-world scenario where different parts of an application need "tagging" functionality, but they don't share a common base class. You need to implement a Taggable module and integrate it into two separate classes: BlogPost and Product.
Requirements:
- Create a module named
Taggable. - Inside
Taggable, implement a methodadd_tag(tag)that adds a string to an array called@tags. (Assume@tagsis initialized as an empty array in the class). - Implement a method
tagsin the module that returns the current list of tags. - Create a
BlogPostclass and aProductclass. Both should use theTaggablemodule so that they can both handle tags. - Ensure that calling
BlogPost.new.add_tag("Ruby")does not affect an instance ofProduct. - Bonus: Add a class method to the
Taggablemodule calledversionthat returns "1.0.0", and useextendwithin the classes to make that version accessible as a class method (e.g.,BlogPost.version).
There are no comments for now.