Senior Ruby on Rails Interview Guide
Real Questions, Real Answers, Code Examples, Follow-up Questions, and Senior-Level Explanations
Senior Ruby on Rails Interview Guide

Real Questions, Real Answers, Code Examples, Follow-up Questions, and Senior-Level Explanations
Ruby — Senior Interview Q&A
Q. What is the difference between a class and a module in Ruby?
Answer: A class represents objects and can be instantiated. It supports inheritance through a super class. A module cannot be instantiated and is mainly used for name-spacing or sharing behavior through mixins. In senior-level code, modules are useful for small reusable capabilities, but overusing them can hide dependencies and make method lookup harder to understand.
module Trackable
def track_event(name)
puts "tracking #{name}"
end
end
class Order
include Trackable
end
Order.new.track_event('paid')
Follow-up question: When would you use a module instead of a superclass? Senior-level explanation: Prefer composition and small modules when behavior is shared across unrelated classes. Prefer inheritance only when there is a true is-a relationship.
Q. What is dependency injection and why use it?
Answer: Dependency injection means passing dependencies into an object instead of hardcoding them. It makes code easier to test and change. In Rails, it is useful for payment gateways, clients, mailers, and background-job adapters.
class ChargeCustomer
def initialize(gateway: StripeGateway.new)
@ gateway = gateway
end
def call(order)
@gateway.charge(order.total_cents)
end
end
Follow-up question: How does dependency injection improve testing?
Senior-level explanation: Use sensible defaults for production dependencies while allowing tests to inject fakes.
Q: What are service objects in Ruby/Rails?
Answer: A service object encapsulates a business operation that does not belong cleanly in a model or controller. It improves testability and keeps controllers thin. Good services have clear inputs, explicit outputs, and limited side effects.
class Orders::Cancel
def self.call(order:, actor:) = new(order, actor).call
def initialize(order, actor)
@order = order
@actor = actor
end
def call
@order.transaction { @order.cancel! }
end
end
Follow-up question: How do you avoid creating too many service objects?
Senior-level explanation: Use services for business workflows, not for every two-line method. Keep domain logic discover-able.
Q: What is exception handling best practice in Ruby?
Answer: Rescue the most specific exception you can handle. Do not rescue Exception because it catches system-level errors. Preserve context in logs and avoid swallowing errors silently. In service objects, return structured failures for expected business errors and raise for unexpected system errors.
begin
PaymentGateway.charge(order)
rescue PaymentGateway::CardDeclined => e
order.update!(payment_error: e.message)
end
Follow-up question: When should you raise vs return false?
Senior-level explanation: Expected validation failures should usually be represented as result objects or model errors. Unexpected failures should be raised and monitored.
Q: Explain map, select, reduce, and each_with_object.
Answer: map transforms each element and returns a new array. select filters elements. reduce accumulates a value. each_with_object is often cleaner than reduce when building hashes or arrays because the object is passed through each iteration.
users.group_by(&:role)
counts = users.each_with_object(Hash.new(0)) do |user, hash|
hash[user.role] += 1
end
Follow-up question: Why can reduce be less readable than each_with_object? Senior-level explanation: Senior code favors clarity over clever chaining. Break complex transformations into named methods.
Q: How do you handle unclear requirements?
Answer: I first try to understand the business goal behind the requirement. Then I ask focused questions to clarify edge cases, expected behavior, and acceptance criteria. If something is still unclear, I suggest a simple version first and keep the implementation flexible for future changes. I also document important decisions so the team stays aligned.
Q: How do you manage deadlines and code quality?
Answer: I try to balance delivery and quality by first understanding the priority. For urgent tasks, I focus on a clean and safe solution without over-engineering. For larger features, I break the work into smaller steps, write tests for important flows, and keep the code maintainable. I also communicate early if something may affect the deadline.
Q: Describe a complex project you worked on.
Answer: One complex project I worked on involved building and maintaining a Rails-based application with database integration, background jobs, validations, and frontend interactivity using Stimulus and Turbo. I was involved in implementing business workflows, improving code structure with service objects, writing validations, fixing production-level issues, and coordinating with other team members. The project required strong understanding of Rails, database design, debugging, and clean code practices.
Q: Write a background job for sending email?
class SendInvoiceJob < ApplicationJob
queue_as :default
def perform(order_id)
order = Order.find(order_id)
OrderMailer.invoice(order).deliver_now
end
end
SendInvoiceJob.perform_later(order.id)
Q: Write a simple service object for payment?
class Payments::ChargeService
def initialize(order)
@order = order
end
def call
ActiveRecord::Base.transaction do
charge_payment
@order.update!(status: "paid")
end
true
rescue StandardError => e
Rails.logger.error(e.message)
false
end
private
def charge_payment
# External payment API call
end
end
Payments::ChargeService.new(order).call
Q: Write SQL to find users with more than 5 orders.
SELECT users.id, users.name, COUNT(orders.id) AS orders_count
FROM users
JOIN orders ON orders.user_id = users.id
GROUP BY users.id, users.name
HAVING COUNT(orders.id) > 5;
Q: Find the second largest number in an array.
def second_largest(array)
array.uniq.sort[-2]
end
second_largest([10, 5, 20, 20, 8])
# 10
Q: Check if a string is palindrome.
def palindrome?(str)
cleaned = str.downcase.gsub(/[^a-z0-9]/, "")
cleaned == cleaned.reverse
end
palindrome?("Madam")
# true
Q: Find duplicate values in an array.
def duplicates(array)
array.select { |item| array.count(item) > 1 }.uniq
end
duplicates([1, 2, 3, 2, 4, 1])
# [1, 2]
Q:What is the difference between save and save!?
Answer:
save returns true or false.
save! raises an exception if the record is invalid.
user.save
# false if validation fails
user.save!
# raises ActiveRecord::RecordInvalid if validation fails
Q: How do you avoid N+1 queries in Rails?
Answer:
I use eager loading with includes, preload, or eager_load. I also check logs or use tools like Bullet gem to detect N+1 issues.
Bad example:
orders = Order.all
orders.each { |order| puts order.user.name }
This may run one query for orders and many extra queries for users.
Better:
orders = Order.includes(:user)
orders.each { |order| puts order.user.name }
Q: What is the difference between includes, joins, and preload?
Answer:
includes is used to avoid N+1 queries and can use either separate queries or a join depending on the condition.
joins creates an SQL join but does not automatically load associated records.
preload always loads associations using separate queries.
메타데이터
- post_id
- 5e6b985f75c9
- slug
- senior-ruby-on-rails-interview-guide-5e6b985f75c9
- url
- https://medium.com/@syedtayyabsagheer/senior-ruby-on-rails-interview-guide-5e6b985f75c9
- canonical_url
- https://medium.com/@syedtayyabsagheer/senior-ruby-on-rails-interview-guide-5e6b985f75c9
- author_url
- https://medium.com/@syedtayyabsagheer
- status
- ok
- fetched_at
- 2026-06-16 19:09:56