LearnThatStack Ace your next interview

Ruby

Ruby 3.0+ on CRuby. Newer rows name their version. Checked Nov 2026 125 code snippets

The model

  • A method call is a message to a receiver, the object before the dot. With no receiver, the call goes to self, the current object.
  • To find a method, Ruby walks the receiver's ancestors and calls the first match. include, prepend and extend change where a module sits in that chain.
  • Only nil and false are falsy. 0, "" and [] are all truthy.
  • The GVL lets one thread at a time run Ruby code. Threads suit I/O-bound work, and processes or Ractors suit CPU-bound work.
singletonprependedthe classincludedsuperclassObjectKernelBasicObjmissing
method lookupThe first match wins. After BasicObject, Ruby calls method_missing.

Strings, symbols & equality

what a value is and how Ruby compares it

Quotes, symbols and printing

Insert a value in a string"Hi #{name}"
Keep the text exactly as written'Hi #{name}'
Print to_s, with or without a newline, return nilputs xprint x
Print inspect, return the valuep x

TipRuby stores each :symbol once, so comparing two is an identity check. Use them for hash keys and fixed labels, and strings for user text.

Naming conventions

Name a method, a class and a constantuser_name, UserAccount, MAX_USERS
Mark a predicate or a more dangerous methodempty?save!

Freeze, copy and value objects

Freeze one object, or a file's string literalsobj.freeze# frozen_string_literal: true
Get an unfrozen copyobj.dup
Copy with frozen state and singleton classobj.clone
Make a small mutable recordStruct.new(:x, :y)
Make an immutable value, with every field required (3.2+)Data.define(:x, :y)

Trapfreeze, dup and clone are all shallow. For plain data, Marshal.load(Marshal.dump(o)) makes a deep copy.

Arrays, hashes & Enumerable

transform, filter and combine collections

Transform and filter an array

Collect block results in a new arraya.map { |n| n * 2 }
Keep the matches, or drop thema.select(&:even?)a.reject(&:even?)
Get matches and the rest in one passa.partition(&:even?)
Remove nesting, or remove nila.flattena.compact

Trapeach runs the block for side effects and ignores its result, so a.each { |n| n * 2 } still returns a.

Blocks, procs & lambdas

pass code around as a value

Proc or lambda

Make a lenient callableProc.new { |x| x }
Make a strict callable to store or pass->(x) { x * 2 }

TipA lambda checks its argument count and raises ArgumentError. A proc fills missing arguments with nil and drops extras.

Trapreturn in a proc exits the method that defined it. In a lambda it exits only the lambda.

Short blocks, then and tap

Skip naming the parameter, with _1, or it in 3.4+a.map { it * 2 }
Transform a value inside a chain5.then { |x| x * 2 }
Run a side effect, keep the objectuser.tap { |u| u.save }

Tipyield_self is another name for then. To chain your own methods, return self from each one.

TrapIf a method returns a number, not self, the next call goes to an Integer and raises NoMethodError.

Arguments & pattern matching

what a method accepts and how data unpacks

Keywords and endless methods

Require one keyword, give another a defaultdef f(name:, age: 18)
Pass a hash as keywords (3.0+)f(**opts)
Write a one-expression method, never a setter (3.0+)def double(x) = x * 2

TrapSince Ruby 3.0 a hash stays positional, so foo({key: 1}) raises ArgumentError when foo takes only keywords.

Classes, self & visibility

how objects hold state and who may call what

Class, module and duck typing

Share methods across unrelated classesinclude Flyable
Check what an object can do before you call itobj.respond_to?(:speak)

TipA module has no new and no subclasses. It's a mixin or a namespace, like Billing::Invoice.

TrapDuck typing means code depends on what an object can do, not its class. A wrong object fails only at run time, with NoMethodError.

Accessors, self and variables

Define a getter, a setter or bothattr_reader, attr_writer, attr_accessor
Store a value per object@name
Share a value with subclasses, which can overwrite it@@count
Store a value on one class, in its body@count

Tipself is the instance in an instance method, the class in a class body, and main at the top of a file.

TrapInside a class, name = value creates a local variable. Write self.name = value to call the setter.

Mixins & metaprogramming

change method lookup and write methods at run time

include, prepend and extend

Add instance methods after the classinclude Mod
Add them before the class, to wrap its methodsprepend Mod
Add singleton methods to one objectextend Mod
List the lookup orderChild.ancestors
Define a method on one objectdef str.shout

TipClass methods are singleton methods of the class object. class << self opens its singleton class.

Trapancestors leaves out the singleton class, which Ruby checks first.

Metaprogramming

Create methods from datadefine_method(name) { |v| v }
Answer calls to undefined methodsmethod_missingrespond_to_missing?

TipPrefer define_method when you know the names in advance. It's faster than method_missing and shows up in methods.

TrapCall super for every name you don't handle, or a typo stops raising NoMethodError.

Eval blocks and refinements

Add an instance method to every stringString.class_eval { def a; end }
Add a method to one objectobj.instance_eval { def a; end }
Scope a monkey patch, then turn it onrefine String dousing Patches

TipA refinement's scope is lexical, so code in another file doesn't see it.

TrapOn a class the result looks reversed, because String.instance_eval defines a class method.

Errors, files, shell & gems

handle failure and reach outside the program

Files and shell commands

Open a file and close it for youFile.open("f") { |f| f.read }
Open a file you must close yourselfFile.new("f")
Run a command, get true, false or nilsystem("ls")
Capture standard output as a string`ls`
Replace the Ruby process, and never returnexec("ls")

TipWith user input, write system("ls", dir), so no shell runs.

TrapA missing file raises a SystemCallError, not an IOError. File.exist? is only a hint, so still rescue.

Gems, require and debugging

Load a gem once per processrequire 'json'
Run a file again on every callload 'config.rb'
Install locked versions, or move to newer onesbundle installbundle update
Set a breakpoint (3.1+)binding.break
Set a breakpoint in an older projectbyebug

TipThe Gemfile lists accepted version ranges, and Gemfile.lock the exact versions. Commit both. binding.irb opens a console at that line.

Traprequire './x' breaks when the program starts from another directory. Use require_relative.

Threads, memory & speed

what senior interviews add about the runtime

The GVL and Ractors

thread ArunI/O waitrunthread BrunrunRactorrun

Thread A releases the GVL while it waits on I/O, so thread B runs. A Ractor has its own GVL and runs at the same time.

Run CPU-bound work in parallelRactor.new { work }

TipMRI calls the lock the GVL, not the GIL. Ruby 4.0 replaced Ractor#take with Ractor#value.

TrapRactors are still experimental, so production CPU work usually runs in several processes.

Threads, fibers and safety

Run code that hands back control itselfFiber.new { Fiber.yield }
Overlap I/O waits with a scheduler gem (3.0+)Async { fetch(url) }
Let one thread at a time run a blockm.synchronize { n += 1 }
Hand work between threads with no explicit lockQueue.new

TipThreads are preemptive, and fibers are cooperative.

TrapThe GVL doesn't prevent race conditions. A thread can switch in the middle of @n += 1.

Memory and speed

Dump the heap to compare over timeObjectSpace.dump_all
Memoize, unless the result can be nil or false@r ||= compute
Turn on the JIT compilerruby --yjit app.rb
Time a blockBenchmark.measure { work }
See where the time goes, at low coststackprofvernier

TipMark-and-sweep frees cycles too. Minor collections mark only young objects.

TrapA leak is an object that stays reachable, like a cache that only grows. Circular references aren't a cause.

Rails & Active Record

the request path, queries and saving safely

render, redirect and strong parameters

Send the browser to a new URL after a saveredirect_to @user
Show the form again after a failed saverender :new, status: :unprocessable_entity
Block mass assignment, allowing only listed keysparams.require(:user).permit(:name)
Require and permit in one call, in Rails 8params.expect(user: [:name])

Traprender :new doesn't run the new action. It keeps this action's instance variables, and a redirect loses them.

N+1 queries and scopes

Eager load and let Rails pick the strategyUser.includes(:posts)
Load in one query with a LEFT OUTER JOINUser.eager_load(:posts)
Name a reusable queryscope :active, -> { where(active: true) }

TipA scope always returns a relation, so it chains. A class method that returns nil breaks the chain.

Trapcount always runs a SQL COUNT, even on loaded records. Use size on a preloaded association.

Callbacks, concerns and services

Send an email after the commitafter_create_commit :send_email
Share scopes and callbacks across modelsextend ActiveSupport::Concern
Wrap one business operation in a plain classSignUp.new(params).call

TipA concern still adds methods to the model. A service object moves the logic out.

TrapWith after_create, the email goes out even if the transaction later rolls back.

Transactions and locking

Commit all writes or noneActiveRecord::Base.transaction do
Catch conflicting saves with an integer columnlock_version
Hold a row lockorder.with_lock { ship }

Tipraise ActiveRecord::Rollback undoes the block quietly.

TrapPlain save returns false without raising, so the other write still commits. Use save! inside the block.

Design, tests & security

patterns, test doubles and common attacks

Composition, injection and Demeter

Take a collaborator from outside, with a defaultdef initialize(mailer = Mailer.new)
Hide a chain like a.b.c behind one method (Rails)delegate :street, to: :address

TipComposition is the usual choice in Ruby. Use inheritance when the subclass really is a special kind of the parent.

SOLID and the Singleton pattern

Single responsibilityA class has one reason to change.
Open/closedAdd new classes, and don't edit working code.
Liskov substitutionA replacement honors what callers expect.
Interface segregationSmall modules, because Ruby has no interfaces.
Dependency inversionPass collaborators in.
Allow one instance, read with Config.instanceinclude Singleton

TrapA singleton is global state, so one test can affect another.

Attacks and sensitive data

Stop SQL injectionUser.where(name: params[:name])
Encrypt a column at restencrypts :ssn

TipRails escapes view output, so XSS risk sits in html_safe and raw. An authenticity token blocks CSRF.

TrapMarshal.load on untrusted data can lead to remote code execution.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 91 questions it links, not just the 13 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.