71 Rails interview questions with detailed answers, sorted from basic to advanced. Free to practice, with progress saved automatically.
Ruby on Rails is a full-stack web framework in Ruby that follows MVC and favors convention over configuration. Topics include its design philosophy, routing with resources, controllers and view partials, Active Record associations and the query interface, eager loading, migrations, Active Storage, batch processing, and testing along with background jobs.
and 9 more topics.
Ruby on Rails (Rails) is a web framework written in Ruby, created by David Heinemeier Hansson (DHH) in 2004. Rails follows the MVC pattern: the Model handles data, the View renders HTML, and the Controller receives the request and connects the two. The Model usually…
Rails divides an application into three layers: Layer Responsibility Example --------- Model Data + business logic; communicates with DB via Active Record app/models/user.rb View Renders HTML for the user (ERB/Haml/Slim) app/views/users/show.html.erb Controller Receives request, calls model, selects view to render app/controllers/userscontroller.rb Request lifecycle: MVC enforces…
Key directories to know: The layout is fixed because Rails follows Convention over Configuration: name and place files by convention and Rails wires the pieces together itself. For example, model User lives in app/models/user.rb and maps to the users table automatically, with no explicit mapping.
A Gem is a Ruby library (package) distributed via RubyGems.org. Rails itself is a gem. In a project, dependencies are declared in the Gemfile and managed by Bundler. Gemfile.lock records exact versions in use — commit this file to ensure every team member uses the…
resources :posts automatically generates 7 standard RESTful routes: HTTP Verb Path Action Helper ------------ GET /posts index postspath GET /posts/new new newpostpath POST /posts create postspath GET /posts/:id show postpath(id) GET /posts/:id/edit edit editpostpath(id) PATCH/PUT /posts/:id update postpath(id) DELETE /posts/:id destroy postpath(id) Use only: /…
render builds the view within the current request — no new request, URL stays the same, instance variables remain intact. redirectto sends an HTTP 302, telling the browser to make a new request to another URL — the current controller finishes, and the new action…
beforeaction is a callback that runs before one or more controller actions — used to share logic across actions, such as authentication, authorization, and record loading. If a beforeaction calls render or redirectto, the callback chain halts and the original action does not run. Other…
Nested resources are routes declared inside each other, expressing a parent-child relationship right in the URL: /posts/:postid/comments means the comments of one specific post. Generated routes: GET /posts/:postid/comments, POST /posts/:postid/comments, DELETE /posts/:postid/comments/:id. In CommentsController the parent id is in params[:postid], so the controller loads the…
Strong Parameters (Rails 4+) force the controller to explicitly declare which parameters are allowed to be assigned to a model — preventing mass assignment vulnerabilities (an attacker sending hidden params like admin=true to escalate privileges). Any un-permitted param is silently ignored. If a required field…
Active Record is the ORM (Object-Relational Mapper) of Rails: each Ruby model class maps to a DB table, and each instance maps to a row. Naming convention: Rails follows Convention over Configuration (CoC), meaning it derives the table name from the class name, plural and…
Active Record has 6 association types: belongsto, hasone, hasmany, hasmany :through, hasone :through, and hasandbelongstomany. Examples of the three common relationship shapes: Foreign key rule: whichever side has belongsto — that table holds the foreign key. Use hasmany :through (3 tables) when the join table…
N+1 occurs when loading a collection then iterating to fetch each item's association → 1 query for the list + N sub-queries. includes vs eagerload vs preload: - includes → Rails picks the strategy automatically. - preload → always 2 separate queries. - eagerload →…
Validations run before save / create / update. On failure, the record is not persisted and errors live in record.errors. Use save! / create! to raise ActiveRecord::RecordInvalid instead of returning false — better in business logic where you want fail-fast behavior.
A callback is code hooked into a point of a record's lifecycle (before or after validate, save, create, update, destroy). Active Record calls them for you, so you do not have to remember to call them by hand everywhere. Callback order per operation: Operation Order…
A scope is a named query defined on the model that returns an ActiveRecord::Relation, so it can be chained with other scopes or conditions. The difference from a class method shows up when the body returns nil, which is common when filtering by an optional…
A migration is a Ruby class that describes a DB schema change over time — it can be applied (up/change) or rolled back (down). Why not raw SQL: - Version-controlled (each migration is a committable file). - Portable: the change method auto-generates SQL for any…
db/schema.rb is a snapshot of the current schema of the DB. Rails regenerates this file after every db:migrate. It is not a migration but the final state; migrations are the steps that lead to that state. Note that t.timestamps in the migration is recorded as…
Use change for operations that are inherently reversible — Rails knows how to invert them: Some operations are not reversible — write explicit up/down: Non-reversible operations: changecolumn (Rails does not know the old column type), execute with raw SQL, and removecolumn without a type. Pass…
An index is an auxiliary data structure (B-tree) that lets the DB find rows by column value without a full table scan. When to add an index: - Foreign key columns (userid, postid) — always needed for fast JOINs. - Columns frequently appearing in WHERE…
A partial is a reusable ERB fragment — its filename starts with (underscore). When passing a collection (render @posts), Rails iterates automatically and uses a local variable matching the partial name (post for post.html.erb) — no .each needed in the view.
flash stores a short message for the next request — typically used after redirectto. flash.now stores it for the current request — used with render (no redirect). No manual cleanup is needed: a value set with flash lives for exactly one following request and is…
CSRF (Cross-Site Request Forgery) is an attack that tricks the victim's browser into sending a request to a server on their behalf. Rails defends by embedding an authenticity token (a random string unique per session) in every form. The controller verifies the token before every…
A Concern is a module included in multiple classes to share logic. Rails provides ActiveSupport::Concern to reduce boilerplate. Model Concerns work the same way — commonly used to share scopes, callbacks, or helper methods across models. Concerns are useful for logic shared in many places,…
A cookie is a small piece of data stored in the browser and sent along with every request. A session is per-user data kept across requests, which Rails accesses through session[...]. A session needs somewhere to live, and by default that place is a cookie.…
Rails ships with Minitest. Most real-world projects use RSpec instead. Test type Scope Tool --------- Unit (model) Tests 1 model, no browser type: :model (RSpec) / ActiveSupport::TestCase Controller/Request Tests actions, params, response codes type: :request (RSpec) Integration Multiple layers, real DB, no browser ActionDispatch::IntegrationTest System…
Active Job is a unified interface for creating and enqueuing background jobs — it can run with different backends (Sidekiq, Resque, GoodJob) without changing the job code. Why it's needed: some tasks shouldn't run inline in an HTTP request because they are slow or failure-prone…
Active Job is an abstraction layer — it defines a common interface that Rails ships with. Sidekiq is a concrete backend using Redis to store and process jobs — the most popular choice for Rails. Sidekiq uses multi-threading (5 threads per process by default since…
For jobs written with Active Job, rspec-rails provides the haveenqueuedjob matcher. It only works when the queue adapter is :test, so set config.activejob.queueadapter = :test in config/environments/test.rb; if the adapter is :sidekiq, RSpec raises an error asking you to switch to :test. This test only…
Action Mailer creates and sends email from Rails in the style of a controller: each mailer method is one email with its own view. The email view lives at app/views/usermailer/welcome.html.erb. In the test environment, emails are not really sent but collected in ActionMailer::Base.deliveries. deliverlater only…
The Asset Pipeline (Sprockets) is a framework for concatenating, minifying, and fingerprinting static assets (JS, CSS, images) before deployment. Main roles: - Concatenation: merges multiple JS/CSS files into one → fewer HTTP requests. - Minification: strips whitespace/comments → smaller file size. - Fingerprinting: appends a…
A helper method is a Ruby method available in views/templates for generating HTML tags and links. The delete button uses buttonto because it renders a small form that sends a real DELETE request. The old linkto ... method: :delete, data: { confirm: ... } style…
Both group routes but affect different things: namespace :admin scope "admin" --------- URL prefix /admin/posts /admin/posts Controller Admin::PostsController PostsController Named helper adminpostspath postspath Use namespace for admin panels (full separation). Use scope when you only want a URL prefix (such as /v1) while keeping existing…
A polymorphic association lets a model belongsto multiple other models through a single association. Example: Comment can belong to either Post or Video. The commentabletype column stores the class name; commentableid stores the id. Rails handles filtering automatically.
Single Table Inheritance (STI) stores several subclasses in one table, using a type column to record which class each row belongs to. Vehicle.all returns the right subclasses because, when loading each row, Active Record reads the type column and builds an instance of the class…
A transaction guarantees all-or-nothing DB operations — if any step fails, everything is rolled back. Every save / create / destroy is automatically wrapped in a transaction. You need an explicit one when several DB operations depend on each other, such as a money transfer…
Referencing a model class (e.g. User.updateall(...)) in a migration is dangerous because the model evolves over time — old migrations break when replayed on a fresh DB because the model no longer has that method/association. Wrong: Right — declare a dedicated model inside the migration,…
A layout is the HTML frame around a view, holding the shared parts of a page such as the header, footer and nav. By default every action uses app/views/layouts/application.html.erb; yield is where each action's view is inserted. Select a different layout per controller: A child…
Fragment cache caches the HTML of one part of a view instead of the whole page. Wrap that part in cache record do ... end and Rails stores the HTML under a key built from the record (cachekeywithversion, based on updatedat) plus the template digest.…
Content negotiation means one action returns different formats (HTML, JSON...) depending on what the client asks for. The client asks through the Accept header or an extension in the URL (.json), and respondto picks the branch that matches. GET /posts/1 returns HTML. GET /posts/1.json, or…
Factory Bot (formerly FactoryGirl) is a gem for creating flexible test data — replacing fixtures (static YAML files) with Ruby code. Fixtures load everything into the DB before tests run — fast but rigid and hard to understand relationships. Factory Bot creates records on demand…
Both replace a real dependency with a fake in a test; they differ in whether the test checks the call. - Stub only defines what a method returns and does not care whether it is called. Use it to isolate something slow or unreliable: HTTP…
DatabaseCleaner is a gem that cleans the database between tests, so data left by one test does not make another pass or fail by accident. Three strategies: - :transaction: opens a transaction before the test and rolls it back after. Fastest, because nothing is actually…
Both remove a record but take different paths: - destroy: loads the object, runs callbacks (beforedestroy, afterdestroy, aftercommit) and handles associations declared with dependent: :destroy. This is the "safe" path when dependent logic exists. - delete: issues a single SQL DELETE, skips callbacks and validations,…
The difference is their relationship to the database transaction: - aftersave: runs inside the transaction, right after the INSERT/UPDATE is issued but before COMMIT. If the transaction later rolls back, DB changes are undone — but external side effects (sent email, enqueued job) cannot be…
Both model a many-to-many relationship; they differ in whether a join model exists: - hasandbelongstomany (HABTM): needs only a join table (e.g. authorsbooks) with no model behind it, and the join table has no primary key. Compact when the relationship is a pure "link" with…
Active Storage is Rails' built-in framework for attaching files (images, PDFs...) and uploading them to cloud services (S3, GCS, Azure) or local disk. Run rails activestorage:install once to create the activestorageblobs and activestorageattachments tables. Declare it on the model and attach: Takeaways: - The actual…
Scaffolding is a generator that produces the entire CRUD stack for a resource with a single command: model + migration + controller + views + route + tests. After this you immediately get: - app/models/post.rb and a migration creating the posts table. - A PostsController…
They differ in three ways: whether validations run, whether callbacks run, and how failure is reported. Method Validation Callback On failure ------------ save, update Yes Yes Returns false save!, update! Yes Yes Raises ActiveRecord::RecordInvalid updateattribute No Yes Returns false only if a callback halts it…
pluck reads the column straight from the DB and returns a plain array of values without instantiating Active Record objects. - map loads every column and builds a full model per row — the heaviest on memory. - select still builds models but only loads…
A Relation is a description of a query that has not run. Rails only sends SQL when you ask for real data — that is called loading. Triggers the query: toa, each, first, last, find, count, sum, pluck, any?, or rendering it in a view.…
Use findeach. User.all.each loads all 5 million rows into memory before iterating — an easy way to get the process OOM-killed. findeach works in batches (1000 records by default), loading one batch, processing it, then releasing it: Note: findeach forces ordering by primary key and…
All three eager-load associations to avoid N+1; they differ in the SQL produced. - preload — always issues a separate query per association (SELECT ... WHERE postid IN (...)). Cannot filter on the associated table's columns. - eagerload — collapses into one query using a…
Callbacks are reasonable when the work is an intrinsic property of the record and must hold in every context: normalizing data (beforesave { self.email = email.downcase }), generating a slug, cleaning up attachments on destroy. Avoid them when the callback causes external side effects: -…
Rails.cache.fetch reads the cache by key. If it is missing (a cache miss) it runs the block, stores the result and returns it; if it is present (a cache hit) it returns the value without running the block. The key must include everything that changes…
Yes, but only because Active Job does not ship the object — it serializes it to a GlobalID (a string like gid://app/User/42) and the worker re-finds it from the DB at run time. Consequences worth knowing: - If the record is deleted before the job…
A Rack middleware is an object with call(env) that returns [status, headers, body] and holds a reference to the next middleware (@app). Register it in config/application.rb, choosing one of the three lines below (writing all three inserts the middleware three times): The header name is…
They differ in whether SQL runs and whether records are loaded into memory. - count — always issues SELECT COUNT(), even if the association is already loaded. Keeps no records. - length — always loads every record into an array, then counts in Ruby. -…