Skip to content

Ruby Developer interview questions

100 real questions with model answers and explanations for Ruby Developer candidates.

See a Ruby Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

lambdablocks

A lambda returns control to its caller, while a non-lambda Proc tries to return from the method that created it.

  • A return inside -> { return 1 } ends that lambda, so the enclosing method can continue with its next line.
  • A return inside Proc.new { return 1 } exits the defining method, even when the Proc is called by another helper.
  • Calling that Proc after its defining method has already returned raises LocalJumpError, so I use lambdas for callbacks that may outlive the current call.

Why interviewers ask this: The interviewer is checking whether the candidate can predict non-local control flow instead of treating Proc and lambda as interchangeable callables.

lambdablocks

A lambda enforces method-like arity, while a regular Proc handles positional arguments leniently.

  • ->(id, status) {} raises ArgumentError when called with one or three arguments.
  • proc { |id, status| } assigns nil to a missing status and ignores extra positional arguments.
  • I choose a lambda for a payment callback contract because an arity error exposes an integration mistake immediately.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands callable contracts and can choose strict or lenient behavior deliberately.

closuresruby

A block or Proc closes over the surrounding local binding, not a frozen snapshot of each value.

  • If rate is changed from 0.1 to 0.2 after discount = ->(price) { price * rate }, later calls use 0.2.
  • State can intentionally persist, as in a counter lambda that increments a captured count on every call.
  • Shared mutable captures are unsafe across threads, so I put cross-thread state behind a Mutex or Queue instead of closing over a Hash.

Why interviewers ask this: The interviewer is checking whether the candidate can reason about lexical scope, lifetime, and mutation in real closures.

rubyblocks

I use yield for a simple immediate callback, &block when I need a Proc object, and ... when a wrapper must forward the complete call unchanged.

  • yield avoids materializing a Proc and pairs with block_given? for an optional block.
  • &block is justified when passing the callback to records.each(&block) or storing it for later execution.
  • def instrument(...); measure { target(...); }; end forwards positional arguments, Ruby 3 keyword arguments, and the block without rebuilding their signatures.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands both block semantics and safe delegation under Ruby 3 argument rules.

include places a module after the class in lookup, while prepend places it before the class.

  • With include Auditable, Order#save wins over Auditable#save and can call super to reach the module only through its own implementation chain.
  • With prepend Auditable, Auditable#save runs first and super reaches Order#save, which makes prepend useful for a narrow wrapper.
  • Order.ancestors reveals the effective order, so I inspect it before assuming which concern or gem method wins.

Why interviewers ask this: The interviewer is checking whether the candidate can predict method dispatch and use prepend without accidental overrides.

ruby

super continues from the current method owner to the next matching method in the receiver's lookup chain.

  • It does not simply call the superclass, because a prepended or included module may be the next owner in Account.ancestors.
  • Bare super forwards the current positional arguments, keywords, and block, while super() forwards none.
  • I use method(:charge).super_method to inspect a surprising chain instead of hard-coding BaseAccount.instance_method(:charge).bind_call(self).

Why interviewers ask this: The interviewer is evaluating whether the candidate understands cooperative method composition beyond basic inheritance.

ruby

A singleton method belongs to one object's singleton class and is searched before methods from its ordinary class.

  • def gateway.timeout; 2; end changes only that gateway object, not every instance of Gateway.
  • class << self defines class methods because the class object itself also has a singleton class.
  • I avoid per-record singleton methods for domain behavior because they make serialization, reuse, and test setup harder than a normal collaborator.

Why interviewers ask this: The interviewer is checking whether the candidate can connect singleton methods to Ruby's object model and judge when they are appropriate.

public_send preserves the receiver's public API, while send can invoke private and protected methods.

  • report.public_send(formatter_name) is appropriate only after formatter_name is mapped through an allowlist such as json or csv.
  • user.send(:password_digest) bypasses visibility, so using send with request data can expose internals.
  • I use send only for intentional framework-level metaprogramming where crossing the visibility boundary is explicit and tested.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands visibility as a runtime boundary in dynamic Ruby code.

define_method creates an instance method from a block and retains that block's lexical closure.

  • A serializer builder can capture field in fields.each and define methods such as serialize_total without string evaluation.
  • The generated method should still have a meaningful name and backtrace, so I prefer define_method(:serialize_total) over broad anonymous magic.
  • I set public, protected, or private deliberately and test the generated API because a loop can otherwise publish many methods accidentally.

Why interviewers ask this: The interviewer is checking whether the candidate can use metaprogramming with lexical scope while keeping the resulting API understandable.

metaprogramming

A dynamic proxy should handle only its recognized method shape, report the same capability through respond_to_missing?, and delegate everything else to super.

  • A locale proxy may translate title_en into record.translations.fetch(:en).fetch(:title), but should reject unrelated names.
  • respond_to_missing?(:title_en, false) makes respond_to?, method, serializers, and test doubles see a consistent interface.
  • Calling super preserves NoMethodError details and prevents a typo such as titel_en from being silently swallowed.

Why interviewers ask this: The interviewer is evaluating whether the candidate can implement dynamic APIs without breaking Ruby introspection or hiding bugs.

A refinement is useful when a small extension must be activated lexically rather than changing the class process-wide.

  • using MoneyFormatting can add Integer#to_money inside one report namespace without changing Integer for Rails or gems.
  • Activation applies to code defined in that lexical scope, so callers do not gain the method merely because the refined method is reached dynamically.
  • I still prefer a plain formatter object for public application behavior because refinement lookup can surprise maintainers and third-party code.

Why interviewers ask this: The interviewer is checking whether the candidate understands the lexical boundary and maintenance trade-off of refinements.

concurrency

I would build a lazy pipeline over File.foreach and stream its final values to the destination with each.

  • File.foreach(path).lazy.filter_map { |line| parse(line) }.map { |row| normalize(row) } retains one source line plus pipeline state instead of arrays for all million lines.
  • The parsing and mapping blocks run only when a terminal operation such as each, first, take, or force requests another value; constructing the pipeline reads nothing.
  • each can write results incrementally and stop early, while to_a or force realizes every retained result and can consume memory proportional to the output.

Why interviewers ask this: The interviewer is evaluating whether the candidate can apply demand-driven iteration to a concrete stream and identify the exact point where work and memory use occur.

ci-cdconcurrency

Enumerator::Lazy defers each stage and pulls only enough source values to satisfy the terminal operation.

  • events.lazy.select(&:billable?).map(&:account_id).take(100).force avoids building intermediate arrays for every event.
  • It can safely work with an unbounded generator when a terminal bound such as take(100) exists.
  • Operations such as sort still require the complete input, so adding lazy does not make a globally ordered infinite stream possible.

Why interviewers ask this: The interviewer is checking whether the candidate understands demand-driven evaluation and its limits, not just the lazy keyword.

validationruby

I use case/in to describe the accepted structures and bind only values that match each shape.

  • in {type: "card", card: {last4: String => last4}} requires the nested keys and a String before using last4.
  • The pin operator, as in {account_id: ^expected_id}, compares against an existing variable instead of rebinding it.
  • Domain objects can support the same API through deconstruct or deconstruct_keys, but I keep patterns small so a schema validator remains clearer for large external payloads.

Why interviewers ask this: The interviewer is evaluating whether the candidate can apply structural matching precisely and knows when it stops being a validation framework.

designapiruby

Ruby 3 treats keyword arguments separately from a final positional Hash, so wrappers must forward them explicitly.

  • create_user({admin: true}) passes one Hash, while create_user(admin: true) supplies a keyword and may target a different signature.
  • A delegator uses *args, **kwargs, &block or the compact ... form rather than assuming *args preserves keywords.
  • **options makes conversion intentional, and **nil can declare that a method accepts no keywords at all.

Why interviewers ask this: The interviewer is checking whether the candidate can avoid subtle delegation failures in modern Ruby code.

railsconcurrency

MRI allows only one thread at a time to execute Ruby bytecode, but threads still overlap when work waits on I/O.

  • Puma threads can serve other requests while one thread waits for PostgreSQL or an HTTP response because those operations release the GVL.
  • Two pure-Ruby image transforms do not run CPU work in parallel, so I use processes or a native extension that releases the GVL for that workload.
  • More threads still consume database connections and memory, so I tune Puma thread count together with the ActiveRecord pool.

Why interviewers ask this: The interviewer is evaluating whether the candidate can distinguish I/O concurrency from CPU parallelism in the common Ruby runtime.

concurrencydata-structures

The GVL does not make a multi-step state change atomic, so a Mutex is needed for shared invariants and Queue is better for handing work between threads.

  • balance = balances[id]; balances[id] = balance + amount can interleave with another thread even though each Ruby operation runs under the GVL.
  • mutex.synchronize protects that read-modify-write sequence, but the critical section should not include a slow HTTP call.
  • Queue provides synchronized push and pop, while SizedQueue also applies backpressure when producers outrun consumers.

Why interviewers ask this: The interviewer is checking whether the candidate understands race conditions at the operation-sequence level and can choose a safer communication primitive.

reactruby

A Fiber scheduler lets one thread cooperatively run other fibers while the current fiber waits on supported nonblocking operations.

  • A server such as Falcon can handle many socket waits without assigning one native thread to every connection.
  • Libraries must use scheduler-aware I/O paths, such as supported socket, sleep, or process-wait hooks, or a blocking native call can still stall the thread.
  • Fibers do not make CPU-heavy Ruby code parallel, so calculating a large report still belongs in a process or background job.

Why interviewers ask this: The interviewer is evaluating whether the candidate understands cooperative I/O concurrency and its ecosystem constraints.

Ractors can run Ruby code in parallel, but mutable objects cannot be shared freely between them.

  • Deeply frozen shareable configuration can be read by several Ractors, while messages otherwise need copying or ownership transfer.
  • Sending a moved object makes it inaccessible to the sender, which prevents ordinary shared-memory races by design.
  • I would isolate a self-contained CPU task first, because Rails state and many gems assume the main Ractor and may not be Ractor-safe.

Why interviewers ask this: The interviewer is checking for a grounded understanding of Ractor isolation rather than claiming it is a drop-in replacement for Rails threads.

gcruby

Ruby's generational GC collects short-lived young objects frequently and scans long-lived old objects less often.

  • A request often creates temporary arrays and strings that die in a minor collection without requiring a full heap scan.
  • References from old objects to young objects are tracked so a minor collection does not miss reachable young data.
  • A growing old generation can raise major GC cost, so I inspect GC.stat and allocation sources before changing GC environment settings.

Why interviewers ask this: The interviewer is evaluating whether the candidate can connect generational collection to normal Rails allocation patterns and measurement.

Locked questions

  • 21

    What problem does Ruby heap compaction address, and why is reducing allocation often the first optimization?

    data-structuresrubyoptimization
  • 22

    Which Ruby profiling tools would you choose to separate CPU time, wall time, and allocations?

    profilingruby
  • 23

    How would you define the boundary of a Rails service object for placing an order?

    rails
  • 24

    What makes an ActiveRecord query object composable?

    queriesorm
  • 25

    When is a form object preferable to adding virtual attributes and callbacks to an ActiveRecord model?

    formscallbacksorm
  • 26

    What conventions does Zeitwerk require from Rails application code?

    rails
  • 27

    How do preload, eager_load, and includes differ for an ActiveRecord association?

    orm
  • 28

    How would you choose an eager-loading strategy for an orders page that filters by customer but renders line items?

  • 29

    What rules keep ActiveRecord scopes reusable and predictable?

    orm
  • 30

    Where should an ActiveRecord transaction begin and end for a multi-model operation?

    transactionsorm
  • 31

    How do nested Rails transactions and after_commit callbacks behave?

    transactionscallbacksrails
  • 32

    How does ActiveRecord optimistic locking protect an edited record?

    lockingorm
  • 33

    When would you use pessimistic locking through lock or with_lock?

    locking
  • 34

    What trade-offs would make you accept or reject Rails single-table inheritance?

    ownershipooprails
  • 35

    What are the main data-model trade-offs of a polymorphic ActiveRecord association?

    orm
  • 36

    How should ActiveRecord connection pool sizing relate to Puma and Sidekiq concurrency?

    poolingormconcurrency
  • 37

    How does Russian doll caching stay correct when a nested Rails record changes?

    railscaching
  • 38

    How would you design a Rails.cache.fetch key for a computed account summary?

    designrailscaching
  • 39

    How would you design Sidekiq job arguments so queued jobs survive a rolling deploy?

    designbackground-jobsdeployment
  • 40

    How should a Sidekiq worker distinguish retryable and permanent failures?

    resiliencebackground-jobs
  • 41

    How would you separate Sidekiq queues for user emails and CPU-heavy exports?

    background-jobsdata-structures
  • 42

    How does GraphQL batching prevent N+1 queries in a Rails schema?

    scheman+1queries
  • 43

    How would you choose between Turbo Frames and Turbo Streams for a Rails interaction?

    rails
  • 44

    What belongs in the design of an authenticated Action Cable channel?

    design
  • 45

    Why are strong parameters not a replacement for authorization in Rails?

    authrails
  • 46

    When does Rails CSRF protection matter, and how does it differ for a bearer-token API?

    csrftokensapi
  • 47

    How do you prevent XSS when Rails renders user-authored rich text?

    xssrails
  • 48

    How would you safely implement user-selected filtering and sorting in ActiveRecord?

    ormalgorithms
  • 49

    How should a Rails application handle secrets and sensitive customer fields?

    secretsrails
  • 50

    How do verifying doubles improve an RSpec test for a Rails service object?

    railstesting
  • 51

    A Rails index endpoint loads 100 orders and logs 101 SQL queries. How do you diagnose and fix it?

    sqlindexesqueries
  • 52

    An ActiveRecord query on 8 million payments takes 2.4 seconds. What do you inspect before changing the code?

    queriesorm
  • 53

    A scope uses joins(:comments) to filter posts, then returns duplicate parent rows. How do you correct it?

    joins
  • 54

    Two checkout requests reserve the last inventory item and both succeed. How would you fix the transaction?

    transactions
  • 55

    An update fails with ActiveRecord::StaleObjectError several times per hour. What does that tell you and what do you do?

    orm
  • 56

    A backfill of 12 million subscriptions repeatedly hits lock timeouts on rows being updated by web requests. How do you reduce contention without stopping traffic?

    resiliencebackfill
  • 57

    Rails raises ActiveRecord::ConnectionTimeoutError during a 25-thread load test, while the pool size is 5. What do you change?

    ormconcurrencyload-testing
  • 58

    A custom tenant helper stores the account in Thread.current, and Puma occasionally serves a request with the previous tenant. How do you fix it?

    concurrency
  • 59

    A vendor starts returning HTTP 429, and its Sidekiq queue grows to 80,000 jobs within an hour. How do you respond?

    procurementhttpdata-structures
  • 60

    A Sidekiq job retries 25 times on ActiveRecord::RecordNotFound. How would you change its retry behavior?

    resiliencebackground-jobsorm
  • 61

    A 40-minute Sidekiq reconciliation restarts from the beginning whenever a deploy terminates the worker. How would you redesign it?

    reactdeploymentbackground-jobs
  • 62

    A vendor call has no timeout and consumes all 30 Sidekiq threads, so unrelated queues stop moving. How do you contain and prevent the failure?

    resiliencebackground-jobsconcurrency
  • 63

    Users can click Export three times and enqueue three identical Sidekiq jobs. Where do you deduplicate them?

    queriesbackground-jobs
  • 64

    After a deploy, Redis grows from 2 million to 18 million keys and starts evicting entries; the new cache key includes the release SHA and raw search parameters. How do you fix it?

    redisdeploymentcaching
  • 65

    A Redis profile cache has a 24-hour TTL, so users see old names after editing. How would you redesign invalidation?

    rediscaching
  • 66

    Redis is healthy, but a dashboard cache hit rate falls from 85% to 2% after a Rails deploy. How do you diagnose the keys?

    redisdeploymentcaching
  • 67

    A GraphQL deploy makes the entire orders field null with an error for non-nullable Order.trackingNumber. How do you debug the contract?

    deploymentgraphqlfundamentals
  • 68

    A GraphQL client asks for 5,000 nested nodes and times out. What guardrails do you add?

    guardrailsgraphql
  • 69

    A Rails process grows from 420 MB to 900 MB over 6 hours at steady traffic. How do you distinguish a leak from allocation churn?

    churnconcurrencyrails
  • 70

    A JSON endpoint allocates 1.2 million objects per request and takes 780 ms. How do you profile it?

    endpoints
  • 71

    Request p99 jumps from 220 ms to 1.1 seconds every few minutes and major GC lines up with the spikes. What do you do?

  • 72

    Will increasing Puma from 5 to 20 threads make a CPU-heavy Ruby endpoint four times faster on MRI?

    concurrencyrubyendpoints
  • 73

    Two Ruby threads occasionally deadlock while moving money between in-memory accounts protected by Mutex. How do you fix it?

    concurrencyrubylocking
  • 74

    A class-level Hash cache is read and written by Puma threads, and entries sometimes disappear. What would you change?

    cachingconcurrency
  • 75

    An RSpec example fails only around midnight or when CI runs in UTC. How do you make it deterministic?

    testing
  • 76

    The RSpec suite passes by file but fails with --order random --seed 18472. How do you debug it?

    testing
  • 77

    A system spec clicks Save and intermittently checks the page before Turbo finishes. How do you fix the test?

    system-design
  • 78

    A Rack middleware verifies a webhook signature by reading the request body, but the Rails controller then receives an empty body. How do you fix it?

    middlewarerailsrack
  • 79

    A constant loads in development but raises NameError only during production eager loading. How do you investigate it?

  • 80

    You must rename users.full_name to display_name without downtime across a rolling Rails deploy. What sequence do you use?

    railsdeployment
  • 81

    You must add a foreign key from 40 million orders to customers, but an audit finds 120,000 orphaned customer IDs. How do you migrate safely?

    foreign-keys
  • 82

    A Rails app uses cookie sessions, and a forged cross-site form can change an email address. What do you verify?

    sessionscookiesforms
  • 83

    An account update endpoint accepts admin=true from the request body. How do you close the mass-assignment hole?

    endpoints
  • 84

    A signed-in user can fetch /projects/92 by changing the ID even though project 92 belongs to another account. How do you fix it?

  • 85

    An orders API uses OFFSET 200000 LIMIT 50 and slows from 40 ms to 1.8 seconds. How do you paginate it?

    api
  • 86

    A client PATCHes an invoice with lock_version 7 after another editor has saved version 8. What should the Rails API return, and how should retry work?

    resiliencerailsapi
  • 87

    A Rails API sometimes returns 500 for invalid nested order parameters. How would you improve the boundary?

    railsapi
  • 88

    A nightly task changes 200,000 subscriptions with update_all, but no audit events are created. How would you redesign it?

  • 89

    A service object sends an email inside a database transaction, but users receive emails for rolled-back orders. How do you fix it?

    databasetransactions
  • 90

    A Turbo Stream response appends the same message twice after a form submission. How do you debug it?

    forms
  • 91

    A Stimulus controller adds another click handler after every Turbo navigation. How do you stop it?

  • 92

    An Action Cable client can subscribe to another user's private notifications by changing user_id. How do you secure the channel?

  • 93

    Action Cable updates arrive 12 seconds late while web requests remain fast. What do you measure?

  • 94

    A webhook job sometimes creates two local delivery records when retried. How would you make the workflow consistent?

    webhooks
  • 95

    A Rails request waits 30 seconds for a partner API and exhausts Puma threads. What is your immediate code-level fix?

    railsapiconcurrency
  • 96

    A Rails search endpoint over 12 million Elasticsearch documents slows from 90 ms to 1.4 seconds after adding a leading-wildcard filter. How do you debug it?

    searchendpointsrails
  • 97

    A model callback enqueues 4 jobs and updates 3 related models whenever an invoice changes. How would you simplify it?

    callbacks
  • 98

    A CSV import of 200,000 rows uses Model.create! in a loop and takes 45 minutes. How do you improve it safely?

  • 99

    A uniqueness validation passes in two concurrent requests, and both try to create the same username. What is the correct fix?

    validationconcurrency
  • 100

    During an incident, checkout errors rise from 0.2% to 8% after a Rails deploy. What do you do in the first 15 minutes?

    incidentsdeploymentrails