Ruby on Rails Interview Questions

Master your next Ruby on Rails interview with our comprehensive collection of questions and expert-crafted answers. Get prepared with real scenarios that top companies ask.

Find mentors at
Airbnb
Amazon
Meta
Microsoft
Spotify
Uber

Master Ruby on Rails interviews with expert guidance

Prepare for your Ruby on Rails interview with proven strategies, practice questions, and personalized feedback from industry experts who've been in your shoes.

Thousands of mentors available

Flexible program structures

Free trial

Personal chats

1-on-1 calls

97% satisfaction rate

Study Mode

Choose your preferred way to study these interview questions

1. How do you create a new Rails application?

To create a new Rails application, you use the rails command followed by new and the name of your application. For example, rails new myapp. This command generates a new Rails app with a default directory structure and takes care of all the initial setup needed to get started. You can also add options to customize the setup, like skipping the test framework with --skip-test or using a specific database with -d postgresql.

2. What is the purpose of helpers in Rails?

Helpers in Rails are designed to keep your views clean and DRY (Don't Repeat Yourself). They allow you to extract complex logic out of the view templates and into reusable methods. This makes your code more readable and maintainable. You typically define these methods in helper modules, which are included automatically in your views. So, whenever you have something that you need to use in multiple views, like formatting dates or creating certain HTML structures, you put that logic into a helper.

3. How do you manage database relationships in Rails?

In Rails, management of database relationships is relatively straightforward thanks to a library called ActiveRecord. ActiveRecord is an Object-Relational Mapping (ORM) system, which means it handles the transfer of data between Ruby objects and database tables. This allows you to use Ruby syntax to perform database operations rather than SQL.

When setting up relationships, ActiveRecord provides methods for four key types of relationships between database tables: 'belongs_to', 'has_one', 'has_many', and 'has_many_through'.

For example, if you have Users and Posts in a blog application, you could say a User 'has_many' Posts, and a Post 'belongs_to' a User. This would set up a one-to-many relationship between Users and Posts.

The key is to properly define these relationships in your model files. By doing so, you can then perform complex database queries using simple Ruby methods, without needing to manually write SQL queries. This makes manipulating and accessing related data more intuitive and less error-prone.

No strings attached, free trial, fully vetted.

Try your first call for free with every mentor you're meeting. Cancel anytime, no questions asked.

Nightfall illustration

4. How would you use Rails’ I18n feature in a web application?

Rails' I18n (short for internationalization) feature allows you to translate your application into multiple languages. It provides a framework to store and retrieve translations, and it comes in handy if your application needs to be localized for different user groups based in different countries.

At the basic level, managing translations with I18n involves defining translations in locale files (config/locales/*.yml), and then fetching these translations in your views, models and controllers.

For example, you could have two locale files, en.yml (for English) and es.yml (for Spanish). Inside en.yml, you'd define:

yml en: hello: "Hello world"

And inside es.yml, you'd define:

yml es: hello: "Hola mundo"

Then in any view, you can use the t (or translate) helper method to retrieve the translation:

erb <%= t('hello') %>

Depending on the current locale (I18n.locale), this will render "Hello world" or "Hola mundo".

To switch locales, you can set I18n.locale to any of the available locales at the beginning of each request, possibly by taking it from the user's preferences, the Accept-Language HTTP header, or the domain name.

That's the essence of it. Rails' I18n is quite feature-rich and allows you to do a lot more than this, such as defining translations in Ruby files, providing default translations, and using variables and pluralization in translations. But for many applications, this simple pattern is enough for most of your needs.

5. What is the difference between a symbol and a string in Ruby?

I’d explain it in two parts, what they are, and when you’d use each one.

  • String is for text content.
  • Symbol is more like an internal label or identifier.

Key differences:

  • Mutability
  • Strings are mutable, you can change them.
  • Symbols are immutable, once created, they do not change.

  • Object identity

  • Two identical strings are usually separate objects.
  • The same symbol name points to the same object.

  • Typical use

  • Use strings for user-facing or editable text, like names, messages, JSON values.
  • Use symbols for fixed identifiers, like hash keys, method names, or options.

Example:

  • "status" is a string, useful if you need to manipulate the text.
  • :status is a symbol, useful if you just need a stable key or label.

Why it matters:

  • Symbols can be more efficient for repeated identifiers because Ruby reuses them.
  • Strings are the right choice when the value is actual content that may change.

A simple Rails example:

  • params[:id], render :show, and belongs_to :user all use symbols because those values are identifiers, not editable text.

6. What is the significance of the 'bundle exec' command?

The bundle exec command in Ruby is used to run a specific command in the context of the current bundle. A bundle, as defined by Ruby's Bundler, is the complete set of gems that your application needs to run, as specified in your Gemfile.

The command bundle exec restricts the command following it (like rails server or rake db:migrate) to the versions of the gems specified in the Gemfile.lock file. This ensures that the commands are executed using the correct versions of the gems, avoiding potential conflicts between different versions of the same gem.

This becomes particularly important in situations where you might have global gems installed on your system that are different versions than the ones your application requires. If you were to simply run rails server, Ruby would use the globally installed gem. But with bundle exec rails server, it runs the command with the version of Rails listed in your Gemfile.lock, preventing potential issues caused by version conflicts. So, it's a good practice to always use bundle exec for running rake tasks, Rails servers, tests or any other command that uses your application's gems.

7. What's the difference between 'belongs_to' and 'has_one' associations in Rails?

Both 'belongs_to' and 'has_one' are types of associations that you can define between two models in Rails. They both establish a one-to-one connection with another model but are applied depending on the relationship between those models.

'belongs_to' is used when the foreign key resides in the model declaring the association. For example, if you have a User model and a Profile model, where every user has one profile and the profiles table has a user_id foreign key, the associations would look like this:

```ruby class User < ApplicationRecord has_one :profile end

class Profile < ApplicationRecord belongs_to :user end ```

On the other hand, 'has_one' is used when the other model contains the foreign key. In the above scenario, User model has_one Profile.

So in essence, 'belongs_to' and 'has_one' both set up a one-to-one connection but 'belongs_to' is used where the foreign key is, while 'has_one' is used on the other side of the relationship.

8. Can you explain how to use modules in Ruby?

Modules in Ruby serve two main purposes: namespace encapsulation and mix-in functionality.

Namespace encapsulation is where you use a module to contain related classes, other modules, or methods, without putting them directly in the global scope. This helps prevent naming collisions. Here's an example:

``` module MyModule class MyClass def say_hello puts "Hello" end end end

MyModule::MyClass.new.say_hello # outputs "Hello" ```

The second purpose is to bundle related methods which can then be included into other classes, behaving as a kind of "mixin". Module methods can be mixed into classes using the include keyword. For example:

```ruby module Greetings def say_hello puts 'Hello!' end end

class MyClass include Greetings end

MyClass.new.say_hello # outputs "Hello!" ```

By including the Greetings module, MyClass now has access to its methods. The cool thing about this is that you can include the same module into multiple classes, offering a degree of code reusability.

Remember, unlike classes, modules can't be instantiated or inherit from one another; their sole purpose is to serve as a container or to be included in classes.

User Check

Find your perfect mentor match

Get personalized mentor recommendations based on your goals and experience level

Start matching

9. Explain how Rails implements MVC architecture.

Ruby on Rails uses the MVC (Model-View-Controller) architectural pattern. This architecture separates an application into three interconnected components, enabling more structured and easy-to-manage code.

The 'Model' part corresponds to all the data-related logic. An ActiveRecord model interacts with the database and participates in handling data, including validations, relationships, transactions, and more.

The 'View' is all about what the user sees and interacts with – the user interface. In Rails, views are typically HTML files with embedded Ruby code that will manipulate and present data.

The 'Controller' serves as the middle-man between the Model and View. It handles the user's request, interacts with the model to fetch or manipulate data, and then chooses the right view to send that data to for presentation to the user.

In a typical Rails interaction, a user interacts with the View, which then raises a request handled by the Controller. The Controller processes the request, interacts with the Model if data manipulation is needed, and then serves a new View back to the user.

10. How do you debug in Ruby on Rails?

I usually debug Rails in layers, from fastest signal to deepest inspection.

My approach is:

  1. Reproduce the issue consistently
  2. Check the logs and request flow
  3. Inspect the data at the point things go wrong
  4. Verify assumptions in the console
  5. Isolate whether it is controller, model, background job, or external service related

In practice, that looks like this:

  • Logs first
    Rails logs usually tell you a lot, request params, SQL queries, timings, exceptions, and stack traces. I’ll also add targeted log lines with Rails.logger.debug or Rails.logger.info if I want to trace a specific branch of logic.

  • Interactive breakpoints
    If logs are not enough, I’ll drop in byebug or binding.pry and inspect the state directly. That helps me check params, model attributes, return values, and whether my assumptions are actually true.

  • Rails console
    I use rails console a lot to recreate the problem outside the request cycle. It is great for testing queries, callbacks, scopes, service objects, and edge cases without clicking through the UI every time.

  • SQL and ActiveRecord inspection
    A lot of bugs come down to unexpected queries or missing associations, so I check generated SQL, eager loading, nil values, and validation failures. If needed, I’ll call methods like valid?, errors.full_messages, or inspect the relation step by step.

  • Tests to narrow it down
    If the bug is tricky, I’ll write or update a spec that reproduces it. That gives me a tight feedback loop and helps make sure the fix sticks.

  • Browser and network tools
    For frontend-related Rails issues, especially with Hotwire, Turbo, or JSON APIs, I’ll also inspect the browser console, network tab, and response payloads.

A concrete example:

I had a case where a form submission looked successful, but the record was not being updated.

Here is how I worked through it:

  • Checked the development log and saw the request was hitting update
  • Looked at the permitted params and noticed one expected field was missing
  • Dropped in a breakpoint inside the controller and confirmed strong params were filtering it out
  • Traced it back to a mismatch between the form field name and the controller whitelist
  • Fixed the param key, reran the request, and then added a request spec so it would not regress

So overall, I start simple, logs and reproduction, then move to breakpoints, console, and tests until I can pinpoint the exact failure.

11. How do you handle assets in Rails?

I usually think about Rails assets in three buckets:

  1. Where they live
  2. How they get built
  3. How they get served

In a typical Rails app:

  • app/assets is for app-specific assets
  • lib/assets is for shared or custom stuff
  • vendor/assets is for third-party files you want to keep in the repo

For styles, scripts, fonts, and images, I rely on Rails conventions and helpers so paths stay clean and fingerprinting works automatically.

A practical way to explain it in an interview is:

  • Start with the default Rails approach
  • Mention how development and production differ
  • Call out the tools you actually use, not every possible option

My answer would be:

Rails has traditionally used the asset pipeline to manage CSS, JavaScript, images, and fonts. It gives you a clean way to organize assets, compile them, and serve optimized versions in production.

In development, assets are easy to work with and usually loaded in a way that makes debugging simple.

In production, Rails precompiles them, adds digested filenames for cache busting, and compresses them so browsers can cache aggressively without serving stale files.

A few things I usually pay attention to:

  • Keep app assets under app/assets
  • Use helpers like stylesheet_link_tag, javascript_include_tag, and image_tag so Rails resolves the right paths
  • Let fingerprinting handle cache invalidation
  • Precompile assets as part of deploys
  • Be intentional about large front-end bundles, especially if the app uses modern JS tooling

If the app is on newer Rails, I also check whether it uses:

  • Importmap
  • jsbundling-rails
  • cssbundling-rails
  • Or still relies mostly on Sprockets

That matters because "handling assets in Rails" can mean different setups depending on the app.

For example, on one project we had Sprockets handling images and CSS, but JavaScript was bundled separately. My job was mostly making sure deploys precompiled correctly, assets were fingerprinted, and CDN caching behaved properly. Most of the work was less about writing asset code, and more about making the pipeline predictable in production.

12. What is the purpose of Yield in Ruby?

The 'yield' keyword in Ruby is used in the context of blocks. It's a way to inject code into a method from the "outside." Whenever Ruby encounters the 'yield' keyword in a method, it pauses execution of the method, runs the code in the block, and then resumes execution of the method. This allows sections of the method to be customized by whoever is calling it, without having to rewrite or modify the method itself.

For example, consider this code:

```ruby def custom_greeting puts "Hello, " yield puts "!" end

custom_greeting { print "world" } ```

The 'yield' keyword in the 'custom_greeting' method allows the 'print "world"' line to be injected into the method, producing the output "Hello, world!". Always remember though, if you include a 'yield' statement, you must provide a block when calling the method, or else Ruby will raise a LocalJumpError.

13. What are Ruby Gems and how do you use them?

Ruby Gems are packages of code that add functionality to your Ruby projects. They serve to extend or modify the functionality in Ruby applications. Gems can range from tiny libraries that provide a single function, to large frameworks like Rails.

Using a gem is simple. First, you find a gem that provides the functionality you need, usually by searching on RubyGems.org, the main repository of Ruby gems. Once you've found a gem to use, you can install it with the gem install command.

However, in modern Ruby applications, it's common to use Bundler to manage gems. Bundler ensures that the correct versions of each gem - and its dependencies - are used. To use Bundler, you add the name of the gem to your Gemfile, which is a list of all the gems your project needs, optionally specifying a version number. Then, you run the bundle install command, which installs all the gems listed in the Gemfile.

Once the gem is installed, you can use its functionality by requiring it at the top of your Ruby file with require 'gemname'. Then, you can start calling the methods or classes that the gem provides. Each gem has its own set of functionalities and ways of implementation, so refer to each gem documentation for the specific usage.

14. What is ActiveRecord and what does it do in Rails?

ActiveRecord is the default Object-Relational Mapping (ORM) layer supplied with Ruby on Rails. It abstracts and simplifies database operations. Instead of manually writing SQL queries, you can work with your data in terms of Ruby objects and methods, ActiveRecord translates those into the appropriate SQL queries under the hood.

ActiveRecord models in a Rails application represent database tables and are the place where data logic resides. For instance, if you have an User model, you'd potentially have a corresponding 'users' table in the database, and each instance of the User model represents a row in that database table.

ActiveRecord provides a ton of functionality out of the box, including but not limited to CRUD operations (Create, Read, Update, Delete), data validation, querying capabilities, and handling of relationships between different models.

For example, if you want to find all users with the first name "John", instead of writing SQL, you'd use ActiveRecord like this: User.where(first_name: "John"). ActiveRecord turns this into the appropriate SQL, sends it to the database, and gives you the result as User objects.

So, ActiveRecord essentially serves as a bridge between your Ruby code and the database, allowing you to interact with your data in a more intuitive, Ruby-ish way.

15. Can you explain what RESTful routes are in Rails?

In the context of Ruby on Rails, RESTful routes are a way of organizing your app's routes around the REST (Representational State Transfer) architecture. REST is a set of conventions for building HTTP services, often used for APIs. It structures interaction around resources, which are any kind of object, data, or service that can be accessed by the client.

A RESTful route is a route that provides mapping between HTTP verbs (get, post, put, delete, patch) to controller CRUD actions (create, read, update, delete). Instead of thinking of routes in terms of pure URLs, it is more accurate to think of them as routes to specific resources.

To put this into practice in Rails, you use resource routing. This provides a mapping between the HTTP verb, the URL, and the method to be called on the controller. For example, a GET request to /photos might map to the index action in the PhotosController, while a POST request to /photos maps to the create action.

By adhering to these conventions and using resource routing, your Rails application will by default have a RESTful design. This makes it easier to reason about, easier to design, and more amenable to being used as an API down the line, if necessary.

16. How would you create a route in Rails?

In Rails, you would create a route by defining it in the config/routes.rb file. Rails routes are essentially the mapping between HTTP requests to controller actions.

For example, to create a route for viewing a blog post, you might add the following line in your routes.rb file:

get 'posts/:id', to: 'posts#show'

This tells Rails to direct a GET request for 'posts/:id' to the 'show' action of the Posts controller. The ':id' portion is a dynamic segment that will match any number, and that value will be stored in params[:id].

Rails also offers resources keyword that can simplify route creation. For example, calling resources :photos in your routes file would create a full set of RESTful routes for a Photos resource, handling common actions like 'show', 'edit', 'update', 'destroy', and so on. This is a convenient way to map a large number of common routes at once.

Remember to run rake routes (or rails routes in newer versions) in the terminal after defining new routes to ensure they've been set up correctly and see a list of all routes in your application.

17. What is the difference between 'render' and 'redirect_to' in Rails?

In Rails, render and redirect_to are two methods used in controller actions, but they perform quite different tasks.

render is used when you want to show a view to the user. It takes a template name, and Rails will look for that template in the corresponding view folder and show it. For example, render :show will display the 'show' template. This does not create a new request; it simply tells Rails what view to use in the current request.

On the other hand, redirect_to causes a new HTTP request. It tells the browser to send a new request to the route provided. It's like telling a user to visit another URL, maybe because the page they requested requires different parameters or they don't have permission to see it.

For example, after creating a new record, you might redirect_to @record, which will send the user to the 'show' page for the newly created record. Note that unlike render, redirect_to does not stop the execution of the function, so you'll often see a return statement following it, or it will be the last line of a function.

In short, render displays a view as part of the current request, while redirect_to triggers a new request to a different route. Both have their uses depending on what flow you need for your application.

18. How does a block differ from a proc?

A clean way to answer this is:

  1. Start with the big-picture difference.
  2. Mention how they’re used in methods.
  3. Call out the two interview-friendly gotchas, object-ness and return behavior.

Here’s how I’d say it:

A block is just an attached chunk of code you pass into a method. A proc is that idea turned into an actual object.

The practical difference:

  • A block is not an object on its own.
  • A proc is an object, specifically a Proc.
  • A block gets passed implicitly to a method.
  • A proc can be stored in a variable, passed around, and called later.

Example:

  • Block: array.each { |n| puts n }
  • Proc: printer = Proc.new { |n| puts n }, then printer.call(1)

One important detail is method signatures:

  • A method can take a block without listing it as a normal argument.
  • If you want to capture that block as an object, you use &block, which gives you a proc.

Another difference interviewers usually want to hear is control flow:

  • Blocks behave more naturally with the method they’re inside.
  • Procs can have surprising return behavior, because return tries to exit from the surrounding method context.
  • That’s one reason people often compare procs with lambdas too, since lambdas handle return more like a regular method.

So the short version is:

  • Block = lightweight, implicit chunk of code passed to a method
  • Proc = reusable block wrapped as an object

If I wanted to sound especially practical in an interview, I’d add:

  • Use a block when you just want to customize a method call, like each, map, or select
  • Use a proc when you want to save behavior, pass it around, or reuse it in multiple places

19. How does Ruby on Rails handle sessions?

Rails sessions are basically a way to keep a little bit of state between requests.

The usual flow is:

  • A user makes a request
  • Rails loads the session data
  • Your app reads or writes values with the session hash
  • Rails sends the updated session back with the response

By default, Rails uses CookieStore.

What that means:

  • The session lives in the browser cookie
  • Rails signs and encrypts it
  • Users cannot easily tamper with it or read it directly
  • The cookie comes back on each request, so Rails can rebuild the session

In practice, you usually store small pieces of data, not whole objects.

Common example:

  • On login, set session[:user_id] = user.id
  • On later requests, read session[:user_id]
  • If it exists, you know who the current user is

A few important details:

  • Keep session data small
  • Do not put large objects or sensitive business data in there
  • Store identifiers, like user_id, instead of full records
  • Sessions are great for auth, flash-like state, or lightweight user context

Rails can also use other session stores if needed, like cache or database-backed approaches, but cookie-based sessions are the standard default for most apps.

20. What is the difference between 'save' and 'save!' in Rails?

Both save and save! methods in Rails are used to save the record to the database, but they behave differently when the record is invalid.

The save method returns a boolean value. If the record is valid and gets saved successfully in the database, it returns true. However, if the record is not valid, then it won't save the record in the database and it will return false. This method doesn't raise any exception on failure.

The save! method, on the other hand, will raise an ActiveRecord::RecordInvalid exception if the record is not valid. This can be useful when you want to ensure that the record must be saved, and if it isn't, the exception will alert you about it.

So if you want to check whether the record was saved or not and handle this manually, use save. If you want an exception to be raised on failure, use save!. It's important to use them appropriately based on the level of strictness you want to enforce and how you want to handle potential failures.

21. What is a before_action filter and give an example of its use?

A before_action in Rails is a filter that is applied before certain controller actions are triggered. It's a type of callback that allows you to specify a method (or methods) which should run before the designated actions take place.

This is useful for checking preconditions and ensuring necessary conditions are met before an action is run. You might use a before_action to verify a user is authenticated or to find a record in the database that the action will interact with.

Here's an example. Assume you have a PostsController with an edit action that lets a user edit a post. Before allowing the user to edit, you want to make sure they're logged in. This could be done with a before_action like this:

```ruby class PostsController < ApplicationController before_action :authenticate_user!, only: [:edit]

# ...

def edit # code for editing the post end

private

def authenticate_user! redirect_to new_session_path unless logged_in? end

def logged_in? # return true if user is logged in, false otherwise end end ```

In this code, the authenticate_user! method is called before the edit action. If the user is not logged in, they're redirected, and the edit action is never triggered. The only option is used to specify that this before_action applies only to the edit action. Similarly, you can use except to specify actions where the filter should not apply.

22. Can you explain what metaprogramming is in Ruby?

Metaprogramming in Ruby is basically code that writes code, or changes behavior at runtime.

Ruby is really good at this because classes are open, methods are just objects Ruby can work with, and you can define behavior dynamically.

A simple way to explain it in an interview:

  • Start with the definition, code that creates or changes other code
  • Mention why Ruby makes it easy
  • Give 2 or 3 practical examples
  • Tie it back to Rails, because that is where people actually use it

Examples I usually bring up:

  • define_method
  • Lets you create methods dynamically
  • Useful when you want to avoid repeating the same method pattern over and over

  • method_missing

  • Catches calls to methods that do not exist
  • Can be useful, but I would mention that it should be used carefully because it can make code harder to debug
  • If you use it, you should usually pair it with respond_to_missing?

  • Reopening classes or modules

  • Ruby lets you add methods to existing classes at runtime
  • That is powerful, but also something to use carefully

In Rails, Active Record is the classic example.

  • If a model has a name column, Rails gives you methods like name, name=, and query helpers dynamically
  • Things like scopes, associations, and attribute methods rely heavily on metaprogramming under the hood

So my short version would be:

"Metaprogramming in Ruby means writing code that defines or changes behavior dynamically at runtime. Ruby makes this easy with features like define_method, method_missing, and open classes. In Rails, Active Record uses metaprogramming all the time to generate attribute methods, association methods, and query helpers based on your model and schema. It is powerful, but I try to use it carefully so the code stays readable and easy to debug."

23. What are Rails helpers and how would you use them?

Rails helpers are methods that provide a way to extract complex logic out of views and keep the views clear of logic and calculations. These methods are available throughout your view templates, and help you abstract recurring patterns.

For example, suppose your application has a specific date format it needs to display. Instead of doing the date formatting in your view, you could write a helper method:

ruby def formatted_date(date) date.strftime("%A, %d %B %Y") end

And then use it in your view like so:

erb <%= formatted_date(@user.created_at) %>

Typically, your helpers would go inside a file in the app/helpers directory. By default, Rails creates a helper module for each controller in your application.

In addition to your own custom helpers, Rails provides a set of built-in helper methods that assist with tasks such as creating forms, outputting HTML, and managing text.

A key point to remember is that views should remain as logic-free as possible. They should focus on presenting information. When you feel that logic is creeping into your views, that's often a good time to write a helper.

24. What is Rack Middleware and how does Rails use it?

Rack is a Ruby package that provides a minimal, modular, and adaptable interface for developing web applications in Ruby. Middleware in Rack represents a series of components that each accept a request, do something with it, and then either pass it along to the next middleware component or deliver a response back to the user.

Each piece of middleware is like a small filter or operation that the request goes through. This could be for logging, setting up sessions, handling cookies, parsing query parameters, or many other tasks.

In Rails, Rack middleware forms a stack, with the Rails application at the bottom. When a request is received by a Rails application, it is first processed by the Rack middleware at the top of the stack, which then interacts with the next middleware, and this process continues until the request reaches the Rails application.

You can see the middleware stack of a Rails application with the command rake middleware in your terminal. This will give you the list of all the middleware being used by your application, in the order they are called.

A key advantage of Rack middleware is that it allows for reusability of various web components, and it lets you insert, remove, or reorder components as needed, which gives you complete flexibility over how requests are handled in your application.

25. What is the role of a 'respond_to' block in Rails?

The respond_to block in Rails controller actions is used to handle different types of responses that can be requested by a client, depending on the format of the data it needs. It's used in conjunction with format methods like html, json, xml and others to define how the action should respond to each type of format.

For example, you might have a UsersController with a show action that could handle both HTML for web browser requests and JSON for API requests:

```ruby def show @user = User.find(params[:id])

respond_to do |format| format.html # render show.html.erb format.json { render json: @user } end end ```

In the above case, if the request URL ends with .html or no format is specified, it will render the "show" view (show.html.erb). If the URL ends with .json, it will render a JSON representation of the user.

Note that the blocks for the format.html and format.json calls are optional. If you don't provide a block, Rails will use sensible defaults: for html, it will render the appropriate view, and for json, it will attempt to render a JSON view or fall back to to_json on the given object.

So, the respond_to block plays the important role of giving you control over the representation of your resources depending on the requested format.

26. Explain how you can use Memcache with Rails.

Memcache in Rails is basically about keeping frequently used data in memory, so you avoid hitting the database or recalculating the same thing over and over.

The usual setup is pretty simple:

  • Run a Memcached server
  • Add the dalli gem, which is the common Ruby client
  • Tell Rails to use Memcache as the cache store

In Rails, that usually means configuring something like config.cache_store = :mem_cache_store or using dalli, depending on the Rails version and setup.

Once that is in place, you use the normal Rails caching APIs. That is the nice part, your app code stays very Rails-y.

Common ways to use it:

  • Fragment caching in views
  • Cache expensive chunks of HTML so they do not get re-rendered every request
  • Low-level caching with Rails.cache.fetch
  • Great for expensive queries, API responses, computed values, or derived data
  • Session storage, in some setups
  • Though a lot of teams now prefer Redis for that use case

A typical pattern is:

  • Pick a stable cache key
  • Set an expiration like expires_in: 10.minutes
  • Wrap the expensive logic in Rails.cache.fetch

For example, if generating dashboard stats is slow, I would cache it with something like Rails.cache.fetch("dashboard_stats:#{user.id}", expires_in: 15.minutes), then only run the query logic on a cache miss.

A couple of practical things matter too:

  • Cache keys should include anything that changes the result
  • Set TTLs so stale data eventually expires
  • Invalidate or version keys when underlying data changes
  • Do not cache everything, only things that are expensive and reused often
  • Monitor hit rate and memory usage, otherwise you are guessing

So in short, Memcache with Rails is mainly plug it in as the cache backend, then use Rails.cache and fragment caching to speed up hot paths in the app.

27. How do you use ActiveJob in a Rails application?

I use ActiveJob as the Rails-friendly layer for background work, anything I do not want blocking a web request.

Typical use cases: - sending emails - processing images or files - syncing with third-party APIs - generating reports - cleanup or retryable maintenance work

How I usually set it up:

  1. Create a job
  2. Generate it with rails generate job ProcessImage
  3. That gives you a class in app/jobs
  4. Inside perform, I keep the job focused on one responsibility

Example structure: - queue_as :default or a more specific queue like :mailers or :low_priority - def perform(image_id) - Load the record inside the job, then do the work

I prefer passing IDs, not full ActiveRecord objects, because it is safer and avoids serialization issues.

  1. Enqueue it from the app
  2. Use perform_later when I want it async
  3. Use perform_now mostly in development, debugging, or very specific flows

For example: - after a user uploads an image, call ProcessImageJob.perform_later(image.id) - the request returns quickly, and processing happens in the background

  1. Pick a backend ActiveJob is the abstraction, it still needs a queue adapter underneath.

Common options: - Sidekiq, my usual choice in production - Delayed Job - Resque - Solid Queue, if staying close to the Rails ecosystem

Then I configure config.active_job.queue_adapter for the environment.

A couple of practical things I pay attention to: - make jobs idempotent, so retries are safe - use retry_on or discard_on for expected failure cases - separate queues by priority - keep heavy business logic in service objects, the job should orchestrate, not do everything - log enough to debug failures easily

If I were answering this in an interview, I would structure it like this: - what ActiveJob is - when to use it - how you create and enqueue jobs - what backend you pair it with - one or two best practices from real projects

Concrete example: - In a Rails app with file uploads, I used ActiveJob with Sidekiq to process images after upload. - The controller saved the record, then queued ProcessImageJob.perform_later(image.id). - The job loaded the image, generated thumbnails, stored metadata, and updated processing status. - That kept the upload request fast and made retries easy if processing failed.

28. How is CSRF protection managed in Rails?

Cross-Site Request Forgery (CSRF) is a type of attack that tricks the victim into submitting a malicious request on behalf of the attacker. Rails includes built-in CSRF protection in the form of a security token that's added to each form that's generated.

This token is stored in the session and is included as a hidden field in all forms that are generated using Rails form builders. When the form is submitted, Rails checks the token from the form against the token stored in the session.

If the session token and the form token match, Rails accepts the request. If they don't match, Rails rejects the request with a 'Invalid Authenticity Token' error and doesn't execute the intended action. This protects against CSRF attacks because it ensures that only forms that have been generated by the app will be accepted.

This protection is enabled by default, but if you need to disable it for some reason (which is generally not advised), you can use skip_before_action :verify_authenticity_token in the relevant controller.

Also, it's good to know that this CSRF protection does not apply to APIs, because they are typically designed to be state-less, meaning that they don't have sessions. For API requests, you'd use different forms of authentication, like token-based authentication or JWT.

29. What is ActionCable and how can it be used in Rails applications?

ActionCable is a built-in Rails framework that seamlessly integrates WebSockets with the rest of your Rails application. It allows for real-time features to be written in Ruby in the same style and form as the rest of your Rails application, while still being performant and scalable. It's a full-stack offering that provides both a server-side Ruby framework and a JavaScript client-side framework.

With ActionCable you can create features like chat, notifications, live updates, and everything else that needs real-time updates.

Here's a simple example for using ActionCable: Let’s say you're implementing a chat feature. First, you would need a channel. You can generate one with a command like this: rails generate channel Chat speak. This will create a 'ChatChannel' and a client-side JS file for the channel.

The speak method we've added here would be a server-side channel method that gets called by the client.

On the client-side, you can use JavaScript or CoffeeScript to manage interactions. This might include connecting to the channel, sending data, or receiving data:

javasript App.chatChannel.speak(message);

You'll typically use ActionCable in combination with ActiveJob for broadcasting messages to the user in real-time. Any lengthy work would be handed off to ActiveJob to keep the WebSocket connection free.

ActionCable provides a real-time, highly interactive experience for users and it's a very powerful tool to have within the Rails environment.

30. Can you explain the concept of "Convention over Configuration" in Rails?

"Convention over Configuration" in Rails means that the framework makes assumptions about what you're trying to do and sets up defaults, so you don't have to configure everything from scratch. For instance, if you have a model named Post, Rails will automatically look for a database table named posts. This reduces the number of decisions you need to make and the amount of code you have to write, streamlining development. Instead of spending time setting up configuration files, you can focus more on your application’s logic.

31. What is Active Record?

Active Record is the object-relational mapping (ORM) layer in Ruby on Rails. It abstracts away the details of database interactions, allowing developers to work with database records using Ruby objects and methods. Essentially, it maps tables in your database to Ruby classes, and table rows to instances of those classes. This makes it incredibly easy to create, read, update, and delete records without writing raw SQL queries, which speeds up development and makes your code more readable.

32. What is a polymorphic association and when would you use it?

A polymorphic association in Rails allows a model to belong to more than one other model using a single association. This means you can have one model, say Comment, that can belong to both Post and Image models, instead of creating separate associations for each. For example, with polymorphic associations, you could have something like commentable that can refer to either a post or an image.

You would use a polymorphic association when you have a situation where multiple models share a common relationship. It helps to keep your database schema clean and avoids the need for multiple foreign keys for different types of associations. It's particularly helpful when you expect the number of associated models to increase and want to maintain flexibility in your codebase.

33. Can you explain what a migration is and how to create one?

A migration is Rails’ way of changing the database schema in a tracked, repeatable way.

Think of it as version control for your database. Instead of manually editing tables in the DB, you describe the change in a migration file, and Rails applies it for you.

Common things migrations handle: - creating or dropping tables - adding or removing columns - changing column types - adding indexes - adding foreign keys

To create one, you usually use a generator from the command line.

For example: - rails generate migration AddTitleToPosts title:string

That creates a migration file with the change already stubbed out.

Then you run: - rails db:migrate

That applies the migration to your database.

A simple workflow looks like this: 1. Generate the migration 2. Review or edit the file if needed 3. Run rails db:migrate 4. Commit the migration with your code

Why it matters: - everyone on the team can apply the same DB changes - schema changes are tracked in the codebase - you can roll changes forward and, in many cases, back with rails db:rollback

So in short, migrations are how Rails manages database structure safely and consistently over time.

34. What are partials in Rails?

Partials in Rails are a way to break down your view templates into smaller, reusable components. They're great for maintaining DRY (Don't Repeat Yourself) principles in your code. For instance, if you have a complex view that includes a form or a repetitive piece of HTML, you can extract that into a partial and include it wherever needed using <%= render 'partial_name' %>. This makes your main templates cleaner and easier to manage.

35. What is Turbo in Rails 7 and how does it differ from previous versions?

Turbo in Rails 7 is a part of the Hotwire framework, which aims to enhance the speed and interactivity of web applications without relying heavily on JavaScript. Turbo replaces the need for front-end frameworks by handling functionalities like page transitions, form submissions, and real-time updates directly from the server-side.

The major difference from previous versions is how Turbo simplifies app development by reducing the need for client-side code. Turbo Streams and Turbo Frames, for example, allow you to update parts of the DOM without a full page reload or tons of JavaScript. This integrates smoothly with the Rails architecture and enhances performance and responsiveness significantly.

36. How do you manage dependencies in a Rails application?

In a Rails application, dependencies are primarily managed using Bundler. Your Gemfile is where you list all the gems your application needs. When you run bundle install, Bundler installs all the specified gems along with their dependencies and ensures they're available for your application. This process also creates a file called Gemfile.lock which locks the versions of the installed gems, ensuring that the same versions are used in different environments or by other developers working on the project.

37. What is the purpose of the config/routes.rb file?

The config/routes.rb file in a Ruby on Rails application is used to define the routes for your application. Essentially, it maps HTTP requests to specific controller actions. This file tells Rails how to handle incoming URLs and what code to execute when a specific path is requested. For example, you can set it so that a GET request to '/users' routes to the index action in the UsersController, or a POST request to '/users' routes to the create action in the same controller. This way, routes act as the bridge between the browser's requests and your application's responses.

38. How do you test a Rails application?

I usually talk about testing in layers, starting with the stuff closest to the code, then moving outward.

A clean way to answer it is:

  1. Explain your testing philosophy
  2. Break it down by test types
  3. Mention tools you use
  4. Show how it fits into CI and day-to-day development

My approach is pretty pragmatic. I want fast, reliable tests that give confidence without turning into a maintenance burden.

  • Model and service tests for business logic
  • Request or controller-level tests for app behavior
  • System tests for key user flows
  • CI to run everything automatically

In Rails, I’m comfortable with either Minitest or RSpec, but in practice I’ve used RSpec most often. I also like FactoryBot for test data and Capybara for end-to-end flows.

Here’s how I usually think about it:

  • Unit tests
  • I test models, POROs, and service objects heavily
  • Validations, scopes, callbacks, and business rules go here
  • If I have complex logic, I want that covered at this level because it’s fast and easy to debug

  • Request tests

  • I prefer request specs over very controller-heavy testing
  • They verify routing, params, response codes, JSON structure, auth, and permission behavior
  • For APIs, this is one of the biggest areas I focus on

  • System or feature tests

  • I use these for critical user journeys, not every edge case
  • Things like sign up, checkout, or multi-step workflows
  • Capybara is useful here because it tests the app more like a real user would use it

  • Background jobs and mailers

  • I test that jobs enqueue correctly and do the right work
  • Same for mailers, making sure the right emails are generated and sent

  • Non-functional checks

  • If performance is important, I’ll monitor and profile hotspots
  • I usually treat performance more as part of observability and profiling than a huge separate test suite

I also try to keep tests maintainable:

  • Avoid over-testing framework behavior
  • Mock external services, like Stripe or third-party APIs
  • Use factories carefully so tests stay readable and fast
  • Keep system tests focused, because they’re slower and more brittle

And I always wire tests into CI, usually GitHub Actions, CircleCI, or similar, so every PR runs the suite automatically. That helps catch regressions early and keeps the main branch stable.

If I wanted to make it more concrete, I’d say something like:

“In a Rails app, I usually build a test pyramid. Most coverage is around models, services, and request specs, because they’re fast and give strong confidence. Then I add a smaller number of system tests for the most important user flows. I’ve typically used RSpec, FactoryBot, and Capybara, and I make sure the whole suite runs in CI on every pull request.”

39. What are some performance optimization techniques in Rails?

I usually think about Rails performance in layers, not just one trick.

A solid way to answer this is:

  1. Start with how you find the bottleneck.
  2. Talk through the biggest levers, database, caching, background work, app tuning.
  3. Give a quick real example so it sounds practical.

In Rails, the main techniques I’d call out are:

  • Database optimization
  • Fix N+1 queries with includes, preload, or eager_load
  • Add the right indexes
  • Only select the columns you need instead of pulling full records
  • Avoid loading huge collections into memory, use batching like find_each when it makes sense
  • Use tools like Bullet, query logs, and EXPLAIN to spot slow queries

  • Caching

  • Fragment caching for expensive view components
  • Low-level caching for repeated computations or API responses
  • Russian doll caching when nested components change at different rates
  • Make sure cache keys are predictable and invalidation is thought through

  • Background jobs

  • Move non-blocking work out of the request cycle
  • Things like emails, webhooks, report generation, file processing
  • Sidekiq is a common choice, especially for higher-throughput apps

  • App and response optimization

  • Paginate large result sets
  • Avoid unnecessary callbacks and heavy model logic on hot paths
  • Use serializers carefully, they can become expensive
  • Reduce JSON payload size for APIs
  • Precompute or denormalize data if a read-heavy endpoint needs it

  • Infrastructure and runtime

  • Tune Puma workers and threads based on the app profile
  • Use Redis effectively for caching and job queues
  • Put a CDN in front of static assets
  • Keep Rails and Ruby reasonably up to date, newer versions often bring real performance gains

  • Monitoring first

  • I try not to guess
  • APM tools like New Relic, Datadog, or Skylight help you see where time is actually going
  • Performance work is much easier when you can measure before and after

Example:

On one app, we had a slow index page that listed orders with customer and shipment info. The page felt fine in dev, but got slow in production as data grew.

I approached it like this:

  • Measured the endpoint first with logs and APM
  • Found an N+1 issue on customer and shipment associations
  • Added eager loading with includes
  • Added an index on a frequently filtered status column
  • Paginated the results instead of loading everything
  • Cached part of the sidebar that was expensive but rarely changed

That brought response time down pretty noticeably, and just as importantly, reduced DB load. For me, that’s usually the Rails performance story, measure first, fix the biggest bottleneck, then verify the impact.

40. What are callbacks in Rails?

Callbacks in Rails are methods that get called at certain moments during an object's lifecycle, such as before or after it is created, updated, saved, or destroyed. They allow you to trigger logic automatically at these points, making them really handy for things like data validation, logging, or updating related objects. For example, you might use a before_save callback to normalize data before it's persisted to the database.

41. What is a concern in Rails?

A concern in Rails is a way to modularize code, allowing you to encapsulate behaviors or functionalities that can be shared across multiple models or controllers. It leverages Ruby's mixin capabilities through modules. When you have methods that don't necessarily belong to a single model or controller but are used in multiple places, you can put these methods in a concern to keep your code DRY.

For instance, if you have a set of methods handling geolocation that needs to be used in several models, you can create a concern called Geolocatable and include it wherever necessary. This makes your code cleaner and more maintainable because you can modify the shared methods in one place.

42. Describe RESTful routing in Rails.

RESTful routing in Rails is all about mapping HTTP verbs and URLs to controller actions in a consistent way that adheres to REST principles. It allows you to create easily understandable and predictable routes for your resources. For example, a resources :posts declaration in your routes.rb file will generate a standard set of routes like index, show, create, update, and destroy for the PostsController.

These routes map to the standard CRUD (Create, Read, Update, Delete) operations: GET for reading resources, POST for creating them, PATCH/PUT for updating them, and DELETE for removing them. This consistency makes it easier to understand and maintain the code, as you always know what URL structure and HTTP method to expect for any given action on a resource.

43. How do you setup and use a background job in Rails?

I’d answer this in two parts:

  1. Show the basic setup.
  2. Explain when and how you actually call the job in the app.

In Rails, I usually talk about background jobs through Active Job, then mention the queue backend, like Sidekiq, because that’s the most common setup.

A simple way to set it up:

  • Add sidekiq to the Gemfile
  • Run bundle install
  • Configure Active Job to use Sidekiq
  • Make sure Redis is running
  • Start a Sidekiq process alongside the Rails app

Then I’d create a job, usually with something like:

  • rails generate job SendWelcomeEmail

That gives you a job class with a perform method. That perform method is where the async work goes, for example:

  • sending an email
  • calling an external API
  • generating a report
  • processing uploads

A typical job might look like:

  • class SendWelcomeEmailJob < ApplicationJob
  • queue_as :default
  • def perform(user_id)
  • find the user and send the email
  • end

Then to use it, I enqueue it from the part of the app that triggered the work. For example, after a user signs up:

  • SendWelcomeEmailJob.perform_later(user.id)

That’s the key idea. The web request stays fast, and the heavier work runs in the background.

A few practical things I usually mention in interviews:

  • Pass IDs, not full Active Record objects, when possible
  • Keep jobs idempotent, so retries are safe
  • Use background jobs for anything slow or non-blocking
  • Monitor failures and retries, especially with external services
  • If needed, put jobs on different queues for priority handling

If they want a more direct Sidekiq example, you can also create a worker and enqueue with perform_async, but in most Rails apps I prefer Active Job as the interface and Sidekiq as the backend.

44. How do you implement authentication in a Rails application?

I usually answer this in two parts, approach first, then implementation.

  1. Start with the decision
  2. For most Rails apps, I use Devise
  3. If I need social login, I add OmniAuth
  4. If the auth flow is simple but highly custom, I might build it myself with has_secure_password

The key is choosing the level of control vs speed. In interviews, I’d mention both, but make it clear what I’d pick by default.

  1. What I’d do in a real app For a standard Rails app, I’d usually go with Devise because it’s mature, secure, and saves a lot of time.

Typical setup looks like this: - Add devise to the Gemfile - Run the install generator - Generate the User model with Devise - Run migrations - Set up default mailer URLs per environment - Add authentication guards like before_action :authenticate_user!

Then I’d configure only the modules I actually need, for example: - database_authenticatable - registerable - recoverable - rememberable - validatable

  1. Security details I’d call out I’d also mention the things around authentication, not just login itself:
  2. Passwords are hashed, never stored in plain text
  3. Sessions should be secure, with proper cookie settings
  4. CSRF protection stays enabled
  5. Sensitive flows like password reset need expiration and token validation
  6. I’d usually add rate limiting or lockout rules for repeated failed logins
  7. For admin or high-risk actions, I’d consider MFA

  8. If I need a custom solution If the app has very specific requirements, I might avoid Devise and use bcrypt through has_secure_password.

In that case I’d implement: - A password_digest column on users - Login by verifying the password against the digest - Session handling with session[:user_id] - Helper methods like current_user - Authorization checks on protected pages

That gives more flexibility, but I’d be careful because rolling your own auth means you’re responsible for all the security details.

  1. Example answer “In most Rails apps, I’d use Devise because it covers the common authentication flows quickly and securely, things like sign in, registration, password reset, and session management. I’d install the gem, generate the user model, run migrations, and protect routes with authenticate_user!. If the app also needed Google or GitHub login, I’d layer in OmniAuth. If I needed a very custom flow, I’d use has_secure_password with bcrypt, store a password_digest, and manage sessions manually. The big thing is making sure password storage, session security, CSRF protection, and reset flows are handled correctly.”

45. What is the role of the `schema.rb` file?

schema.rb is Rails’ current snapshot of the database structure.

A simple way to explain it:

  • It shows what the database looks like right now
  • Rails updates it automatically when migrations run
  • It includes things like tables, columns, indexes, and basic constraints

Why it matters:

  • You can rebuild the database schema from schema.rb without replaying every old migration
  • It makes it easier for teams to stay in sync
  • It gives you a quick, readable view of the app’s schema

One important detail, schema.rb is the end result, not the source of truth for how changes happened. The migrations are still what describe the step by step changes.

46. How do you handle exceptions in Rails?

I try to handle exceptions at the right level, not just rescue everything and hope for the best.

A few rules I follow:

  • Rescue specific exceptions, not StandardError unless I really mean it.
  • Handle errors as close to the failure as possible if I can recover from them.
  • Let them bubble up if I cannot actually do anything useful there.
  • Log enough context so debugging is easy.
  • Show users a safe, friendly response, not a stack trace.

In Rails, that usually looks like this:

  • In service objects or model-layer logic, I use begin/rescue when I expect something like a third-party API timeout, parsing issue, or payment failure.
  • In controllers, I use rescue_from for app-level concerns like ActiveRecord::RecordNotFound or authorization errors.
  • For truly unexpected failures, I rely on error tracking like Sentry or Honeybadger so the team gets alerted with request context.

Example approach:

  1. Catch expected exceptions where I can recover.
  2. Convert them into something meaningful for the app.
  3. Use rescue_from in ApplicationController for consistent responses.
  4. Log and monitor anything unexpected.

A concrete example, if I am calling an external shipping API:

  • In a service object, I rescue timeout-related errors.
  • I log the order ID and request details.
  • I return a failure result the controller can handle cleanly.
  • In the UI, the user sees something like, "We could not fetch shipping rates right now. Please try again."

For common Rails exceptions, I usually set up things like:

  • ActiveRecord::RecordNotFound mapped to a 404
  • authorization errors mapped to 403
  • invalid record or validation-related issues mapped to 422
  • everything else reported to monitoring, then rendered as a 500

The main thing is to treat exceptions as part of app design, not just error cleanup. The goal is predictable behavior for users and enough visibility for developers to fix issues fast.

47. Explain the difference between `render` and `redirect_to`.

render is used when you want to display a view template without making a new request. It can be used within the same action or after some logic. For example, if you have a form with errors, you might render the form view again to show those errors, while still maintaining the context of the original request.

On the other hand, redirect_to triggers a new HTTP request and tells the browser to navigate to a different URL. This is helpful when you want to follow the Post/Redirect/Get pattern to prevent duplicate form submissions or after successfully processing data and you want to take the user to a different page, like showing a list of items after creating a new one. Essentially, render keeps you in the same request-response cycle, while redirect_to initiates a new one.

48. How do you use environment variables in a Rails application?

Environment variables in a Rails application are typically used to keep sensitive information like API keys, database passwords, or configuration settings out of your codebase. To use them, you can either set them directly in your operating system or use a gem like dotenv-rails.

With dotenv-rails, you create a .env file in the root of your project and define your variables there, like SECRET_KEY=your_secret_key. Then, in your Rails app, you access these variables using ENV['SECRET_KEY']. This way you can keep these configurations out of your source control by adding the .env file to your .gitignore.

49. How do you handle file uploads in Rails?

In Rails, handling file uploads can be efficiently managed using the Active Storage framework. Active Storage allows you to upload files to cloud storage services like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage, as well as to the local filesystem. You start by adding Active Storage to your application through a migration and setting up the necessary configuration. Then, you can attach files to your records using the has_one_attached or has_many_attached methods in your models.

For example, if you want to attach a profile picture to a User model, you would add has_one_attached :avatar to the User model. In your forms, you would include a file field for the attachment. Finally, you can use the avatar method to upload and retrieve the file in your controller and views. This simplifies the complexity around file uploads while providing a powerful and flexible way to manage them within a Rails application.

50. What is Active Job and when would you use it?

Active Job is a framework for declaring jobs and making them run on a variety of queuing backends in Rails. Essentially, it provides a standardized interface for background job processing. You'd use Active Job whenever you have tasks that are time-consuming or resource-heavy and can be performed asynchronously, such as sending emails, processing image uploads, or interacting with third-party APIs.

For example, if your app sends out welcome emails to new users, rather than making the user wait while the email is sent during the signup process, you can create a job for sending the email and enqueue it to be processed in the background. This improves the user experience by speeding up response times and handling tasks more efficiently.

51. Explain how to use Rails migrations to modify the database schema.

Rails migrations are a convenient way to alter your database schema over time in a consistent and easy manner. When you create a migration, it generates a Ruby file with methods that define the changes to be made. You can add columns, create tables, drop tables, and even modify existing columns in your database.

To create a migration, you'd typically run a command like rails generate migration AddFieldToTable field:data_type. This creates a new migration file in the db/migrate directory. Inside this file, you'll have two methods: up and down. The up method defines what changes should be made to the database, and the down method defines how to reverse those changes, which is crucial for rolling back migrations.

To apply the migration, you run rails db:migrate, which will execute the code within the up method. If you need to reverse a migration, you can roll it back using rails db:rollback, which will execute the code in the down method. This structured approach to modifying the database schema helps ensure consistency and makes managing your database changes much simpler over the lifecycle of your application.

52. How do you use the `after_commit` callback in Rails?

The after_commit callback in Rails is used to execute code after a record has been saved and committed to the database. It's helpful for tasks that should only run once the transaction is fully complete, like sending emails or updating external systems. You'd use it in your model like this:

```ruby class User < ApplicationRecord after_commit :send_welcome_email, on: :create

private

def send_welcome_email UserMailer.welcome_email(self).deliver_later end end ```

In this example, send_welcome_email will be called after a new user record is created and committed to the database. The on: :create option specifies that the callback should only run after a create action. You can also use :update, :destroy, or omit the on option to run the callback on any type of save operation.

53. How do you handle pagination in a Rails application?

In a Rails application, pagination can be efficiently handled using gems like Kaminari or WillPaginate. These gems provide helpers that make it incredibly simple to paginate your data sets. With Kaminari, for example, you just add the gem to your Gemfile and run bundle install. Then, in your controller, you can apply pagination to an ActiveRecord query like this:

ruby @items = Item.page(params[:page]).per(10)

And in your view, you can add a pagination control with a single line:

erb <%= paginate @items %>

This approach keeps your code clean and easily adjustable. You can customize the number of items per page, as well as the styling of the pagination controls, to fit the needs of your application and its users.

54. How do you manage sessions in Rails?

In Rails, session management is handled using the session hash, which stores data that you want to persist across multiple requests from the same user. By default, Rails uses a cookie-based session store where session data is stored on the client side inside a cookie that's signed and encrypted for security. You can access and modify session data using simple hash-like syntax, for example, session[:user_id] = @user.id.

For more advanced needs, you might opt for different session stores like ActiveRecord::SessionStore or Redis, especially when dealing with large amounts of session data or needing fast read/write access. Configuring these involves adding the appropriate gem, generating a session migration if needed, and updating your config/application.rb or config/initializers/session_store.rb to point to the new store.

55. Describe the significance and use of `strong_parameters`.

strong_parameters is a feature introduced in Rails 4 to help prevent mass assignment vulnerabilities. By default, it requires developers to explicitly specify which parameters are allowed to be used in ActiveRecord models, which increases the security of the application. This ensures you're not unintentionally allowing users to set attributes that they shouldn't be able to, like admin status or user IDs.

To use strong_parameters, you typically define them in your controller by using the permit method inside a params.require block. For example, if you're dealing with a User model, you might have a private method in your UsersController like this:

ruby private def user_params params.require(:user).permit(:name, :email, :password) end

This way, only the specified parameters (:name, :email, and :password) are allowed through, keeping your model safe from unwanted parameter changes.

56. How do you handle different environments (development, test, production) in Rails?

Rails handles different environments using the config/environments directory, where you have separate files for development (development.rb), test (test.rb), and production (production.rb). These files allow you to define environment-specific configurations.

In addition to these files, Rails also uses the RAILS_ENV environment variable to determine which environment configuration to load. By default, Rails runs in the development environment, but you can switch to test or production by setting the RAILS_ENV variable accordingly when starting your server or running commands. This setup makes it easy to have different settings for things like database connections, logging levels, and email delivery methods, ensuring each environment is configured correctly without manual intervention each time.

57. How do you configure multiple databases in a Rails application?

You configure multiple databases in a Rails application by making use of the database.yml file. This file is located in the config directory of your Rails app. In Rails 6 and later, you can define multiple databases for different environments within this file. Each environment (like development, test, production) can have multiple database configurations specified under different names.

Here's a basic example for a development environment:

```yaml development: primary: database: my_primary_db username: user1 password: password1 host: localhost

secondary: database: my_secondary_db username: user2 password: password2 host: localhost ```

In your application code, you can then establish connections to the different databases using the configurations you've defined, typically managed through ActiveRecord.

58. What are Action Mailer and Action Mailbox used for?

Action Mailer in Rails is used to send out emails from your application. It helps you set up mailer views and templates just like you do for controllers and views, making it super easy to manage email-related functionalities. Think of it like the middleman between your app and the mail server.

Action Mailbox, on the other hand, is designed to receive emails into your application. It routes incoming emails to controller-like mailboxes for processing. So if you need to handle incoming mail, like replies to notifications or support emails, Action Mailbox is the feature you’d use.

59. Can you explain what Ruby on Rails is and why it's useful?

Ruby on Rails is a web framework for building database-backed applications in Ruby.

At a high level, it gives you a strong default way to build things like:

  • user accounts
  • dashboards
  • admin panels
  • marketplaces
  • APIs
  • internal business tools

Why it’s useful:

  • It helps you move fast. Rails gives you a lot out of the box, things like routing, database access, views, background jobs, and testing support.
  • It follows clear conventions. Instead of configuring everything from scratch, Rails has standard ways to name files, structure code, and connect pieces together.
  • It reduces boilerplate. You spend more time building product features, less time wiring up plumbing.
  • It’s built around MVC, so your app logic, UI, and data are separated in a way that stays organized as the app grows.
  • It has a mature ecosystem. There are tons of gems, strong community support, and well-established patterns for common problems.
  • It’s great for rapid development. If a team needs to get from idea to working product quickly, Rails is usually a strong choice.

One of the biggest Rails ideas is convention over configuration.

That basically means Rails makes smart assumptions for you. If you follow the standard structure, a lot of things just work without extra setup. That speeds up development and makes apps easier for other Rails developers to jump into.

Another big benefit is maintainability.

Because Rails apps tend to follow consistent patterns, it’s easier for teams to collaborate, onboard new developers, and keep the codebase clean over time.

So if I had to describe Rails simply, I’d say:

It’s a framework that helps teams build web applications quickly, using sensible defaults, clean structure, and a lot of built-in tools.

60. How would you test your applications in Ruby on Rails?

I’d answer this in layers.

First, explain your testing strategy, not just the tools. Interviewers usually want to hear: 1. What you test. 2. At what level you test it. 3. How you keep tests fast and useful. 4. What tools you actually use in Rails.

My approach is usually:

  • Put most of the coverage around the business logic.
  • Use request or integration specs to verify key user flows and API behavior.
  • Keep full browser or system tests for the most important happy paths.
  • Avoid over-testing framework behavior.

In Rails, I’ve mostly used RSpec, but I’m comfortable with Minitest too. The framework matters less than having a solid test pyramid and keeping the suite reliable.

A typical setup for me looks like this:

  • Model tests for validations, scopes, callbacks, and domain logic.
  • Service or PORO tests for business rules.
  • Request specs for controller behavior, auth, status codes, and JSON responses.
  • System tests for critical end-to-end flows like sign up, checkout, or admin actions.
  • Background job tests for enqueueing and execution behavior.
  • Mailer tests when email content or delivery matters.

I also like to keep tests practical:

  • Use factories or fixtures carefully, don’t create huge object graphs unless needed.
  • Mock external APIs so tests stay fast and deterministic.
  • Add shared examples or helpers when there’s repeated behavior.
  • Run the full suite in CI on every PR.
  • Track flaky tests aggressively, because once a suite feels unreliable, people stop trusting it.

For example, if I were building an e-commerce app, I’d test it like this:

  • A model spec to make sure an order can’t be submitted without line items.
  • A service spec for pricing logic, discounts, tax calculation, and edge cases.
  • A request spec for POST /orders to verify auth, params, and response structure.
  • A system test that walks through adding a product to cart and checking out.
  • A job spec for sending the order confirmation email after purchase.

I usually aim for confidence over raw coverage numbers. I’d rather have a smaller suite that catches real regressions than a huge suite full of brittle tests.

61. How would you handle multiple environments (development, testing, production) in Rails?

I’d answer this in two parts:

  1. Show that you understand what each environment is for.
  2. Explain how you keep behavior consistent while isolating config, secrets, and external services.

A solid answer would sound like this:

In Rails, I treat environments as separate runtime contexts with the same app code, but different configuration.

The default ones are:

  • development, optimized for local iteration
  • code reloading on
  • verbose logging
  • caching usually off
  • developer-friendly error pages

  • test, optimized for fast and repeatable automated tests

  • isolated database
  • predictable settings
  • mailers, jobs, and external calls usually stubbed or faked

  • production, optimized for stability, security, and performance

  • class caching on
  • eager loading on
  • caching enabled
  • stricter logging and error handling
  • real infrastructure like Redis, background jobs, and cloud storage

In practice, I manage that through a few layers:

  • config/environments/development.rb, test.rb, and production.rb
  • environment-specific Rails behavior lives there

  • credentials and environment variables

  • secrets never go in source code
  • things like API keys, database URLs, and S3 config come from Rails credentials or the platform env vars

  • database config

  • each environment gets its own database
  • test should never touch development or production data

  • service boundaries

  • in development and test, I usually avoid hitting real third-party services
  • for example, use local storage instead of S3, letter_opener instead of sending real emails, and stub external APIs in tests

I also try to keep environment differences intentional and minimal. If development behaves too differently from production, you end up with deployment surprises. So I like to mirror production where it matters, especially around caching, background jobs, and integrations.

For example, on a recent app:

  • development used SQLite locally at first, then Postgres once the app matured
  • test had a completely separate Postgres database and mocked external APIs
  • production used Postgres, Redis, Sidekiq, S3, and full error reporting through something like Sentry

If the team needs it, I’m also fine creating custom environments like staging, usually to test production-like behavior before release. But I try not to create extra environments unless there’s a real operational need, because every new environment adds maintenance overhead.

62. How would you use ActiveRecord validations?

I use ActiveRecord validations to put business rules as close to the data as possible.

A simple way to talk about it is:

  1. Define what must be true before a record can be saved.
  2. Use built-in validations for common rules.
  3. Add custom validations when the rule is domain-specific.
  4. Pair app-level validations with database constraints for anything critical.

Example:

If I have a User model, I might validate:

  • email is present
  • email is unique
  • password meets a minimum length
  • username matches a format

In Rails, that looks like using things like:

  • validates :email, presence: true, uniqueness: true
  • validates :password, length: { minimum: 8 }
  • validates :username, format: { with: ... }

A few validations I use a lot:

  • presence
  • uniqueness
  • length
  • numericality
  • inclusion
  • format

A practical example:

On a signup flow, I would validate that the user has a unique email and a valid password before creating the account. If validation fails, Rails prevents the save and puts messages in errors, so the controller or form can show clear feedback to the user.

One thing I always call out, uniqueness validation in Rails is not enough by itself. For anything important, I also add a unique index at the database level to avoid race conditions.

So my rule of thumb is:

  • Use validations for user-facing feedback and model-level rules
  • Use database constraints for data integrity
  • Keep validations readable, and only add custom logic when the built-ins are not enough

63. What is the Rails console and how would you use it?

The Rails console is your app’s interactive sandbox.

It opens a Ruby session with the full Rails environment loaded, so you can work directly with:

  • models
  • database records
  • services
  • mailers
  • app configuration
  • pretty much any application code

You start it with rails console or just rails c.

How I use it in practice:

  • test ActiveRecord queries quickly
  • inspect or update data
  • try out model methods
  • debug business logic
  • reproduce issues without clicking through the UI
  • sanity check background job or service behavior

A few simple examples:

  • User.find(1) to inspect a record
  • User.where(active: true) to test a query
  • User.create!(name: "John") to create data
  • Order.last.total_price to verify model logic

It’s especially useful when debugging. If something looks off in the app, I’ll jump into the console, pull the same records, run the same methods, and narrow down whether the issue is data, validations, callbacks, or query logic.

I also use it carefully in production. It’s powerful, so for production I’d stick to read-heavy investigation unless there’s a controlled reason to make a change.

64. Describe the Rails asset pipeline.

I’d frame it around three things: what it is, what problems it solves, and what Rails is doing behind the scenes.

A clean answer would be:

The Rails asset pipeline is Rails’ way of organizing, processing, and serving front end assets like CSS, JavaScript, images, and fonts.

At a high level, it helps with a few important things:

  • Asset organization, usually through places like app/assets, lib/assets, and vendor/assets
  • Preprocessing, like turning SCSS into CSS
  • Compression and bundling, depending on the setup
  • Fingerprinting assets, so browsers can cache files aggressively without serving stale versions

The big win is performance and maintainability.

For example:

  • Instead of serving raw source files, Rails can compile them into production ready assets
  • It adds a digest to filenames, like application-abc123.css, which makes cache busting easy
  • When an asset changes, the filename changes too, so users get the new version automatically

In production, assets are typically precompiled ahead of time, then served as static files by the web server or CDN.

One thing I’d mention in an interview is that the meaning of "asset pipeline" has shifted a bit across Rails versions. Traditionally this was handled with Sprockets. In newer Rails apps, JavaScript might be handled with tools like jsbundling-rails or import maps, while CSS might use cssbundling-rails. But the core idea is still the same, Rails gives you a structured way to manage and deliver frontend assets efficiently.

65. What are Rails Engines and how are they used?

I’d explain Rails Engines as packaged chunks of Rails functionality.

They are basically mini Rails apps that live inside another Rails app, or can even be shared across multiple apps.

An engine can have its own:

  • models
  • controllers
  • views
  • routes
  • migrations
  • assets
  • background jobs
  • initializers

The main use case is modularity.

If I have a feature that is big enough to stand on its own, or I want to reuse it in more than one app, an engine is a good fit.

A couple common examples:

  • an admin panel
  • a billing module
  • a blog or CMS
  • an internal authentication system

In practice, you build the feature as an engine, package it cleanly, then mount it into a host app with its own route namespace.

So instead of scattering that logic all over the main app, you keep it isolated and easier to maintain.

Why teams use engines:

  • reuse the same feature across projects
  • keep large monoliths more organized
  • separate ownership between teams
  • reduce coupling in the main app

One important detail is that engines are not just plain gems. They are gems with Rails-specific structure and integration, so they plug into the framework much more deeply.

For example, if I had a company-wide admin feature used by several products, I’d likely put that into an engine. Then each app could mount something like /admin, run the engine’s migrations, and get the same behavior without rebuilding it each time.

66. How does caching work in Rails?

Caching in Rails is basically about not doing the same expensive work over and over.

If something is slow to compute, like a query, a rendered partial, or a JSON response, Rails can store the result and reuse it until that data changes.

The main buckets are:

  • View caching
  • Fragment caching stores part of a page, not the whole thing.
  • Very common for things like product cards, sidebars, nav sections, or expensive partials.
  • In views, you usually wrap a block with cache.

  • Low-level caching

  • Lets you cache arbitrary Ruby data with Rails.cache.
  • Good for computed values, API responses, counts, or anything outside the view layer.
  • Example idea: cache a report result for 15 minutes instead of recalculating it on every request.

  • Collection caching

  • A nice optimization when rendering lists of records.
  • Rails can cache each item in a collection and reuse only the ones that did not change.

  • Russian doll caching

  • Nested fragment caching.
  • You cache a parent fragment that contains child fragments.
  • If one child changes, Rails can reuse the rest.

There are also older patterns like page caching and action caching, but in modern Rails, fragment and low-level caching are what you usually talk about first.

A big part of Rails caching is cache invalidation, meaning, when does the cached value get refreshed?

Rails helps with that through cache keys:

  • Active Record objects include timestamps like updated_at in their cache key
  • If a record changes, its cache key changes
  • That means the old cached fragment is skipped and Rails generates a fresh one

Example:

  • If I cache a @product partial, Rails might use a key like products/123-20260316...
  • When that product is updated, the key changes automatically
  • So I do not have to manually clear that fragment in most cases

You also choose a cache store, depending on the environment:

  • :memory_store for simple local setups
  • :file_store for basic persistence
  • :mem_cache_store with Memcached
  • :redis_cache_store with Redis, very common in production

What I usually watch out for:

  • Do not cache something user-specific unless the cache key includes the user context
  • Set sensible expiration for data that changes often
  • Measure first, because caching adds complexity
  • Be careful not to serve stale data longer than the business can tolerate

So in practice, Rails caching is, store expensive results, key them in a way that changes when the data changes, and use the right cache store for the app.

67. How would you handle form-based authentication in Rails?

I’d keep it simple and lean on the Rails conventions.

A solid way to answer this is:

  1. Start with the core pieces, user model, password handling, session management.
  2. Explain the request flow, login form, authenticate, store user in session, logout.
  3. Mention the security basics, hashed passwords, session reset, protected routes.
  4. If you want to sound more senior, add a couple of production concerns like rate limiting and secure cookies.

In Rails, I’d usually handle form-based auth with:

  • a User model
  • has_secure_password for password hashing
  • a SessionsController for login/logout
  • the Rails session cookie to remember who’s logged in

The basic flow looks like this:

  • User submits email and password from a login form
  • In SessionsController#create, find the user by email
  • Call authenticate(password)
  • If it passes, store user.id in session[:user_id]
  • If it fails, re-render the form with an error
  • On logout, clear the session in destroy

A few implementation details I care about:

  • Never store plain-text passwords
  • Use bcrypt with has_secure_password
  • Reset the session on login to avoid session fixation
  • Expose a helper like current_user in ApplicationController
  • Use a before_action like require_login for protected pages

So in practice, I’d have:

  • new for the login form
  • create to sign in
  • destroy to sign out

Then in ApplicationController, I’d add something like:

  • current_user, memoized from session[:user_id]
  • logged_in?
  • require_login

For production, I’d also think about:

  • secure, HTTP-only session cookies
  • CSRF protection, Rails gives you this by default
  • basic rate limiting or lockouts for repeated failed logins
  • generic error messages so you don’t leak whether an email exists

If the app needs more than basic username/password auth, I’d probably reach for Devise or Sorcery instead of building every detail myself. For a standard Rails app though, the built-in session approach is clean, reliable, and easy to maintain.

68. How do you protect against SQL injection in Rails?

I’d answer this in layers, from most common to edge cases.

  • First choice, stick to ActiveRecord query methods.
  • If I have to write SQL, use parameter binding, never string interpolation.
  • Then add validation and least-privilege database access as backup protection.

In Rails, the safest path is using things like:

  • where(name: params[:name])
  • find_by(email: params[:email])
  • User.where("email = ?", params[:email])

Those all keep user input separate from the SQL itself, which is what prevents injection.

What I avoid is anything like:

  • "SELECT * FROM users WHERE email = '#{params[:email]}'"
  • where("email = '#{params[:email]}'")

That’s the classic way to create an injection hole.

If I need raw SQL, I still parameterize it. For example:

  • use placeholders with ?
  • or named bindings
  • or Rails sanitization helpers when appropriate

A few practical rules I follow:

  1. Prefer hash-style ActiveRecord queries whenever possible.
  2. Never interpolate params directly into SQL strings.
  3. Be extra careful with dynamic ORDER BY, column names, or table names, because parameterization does not protect those automatically.
  4. Whitelist any dynamic column or sort input before using it.
  5. Keep validations and database permissions tight, so even if something slips through, the blast radius is smaller.

So the short version is, Rails gives you strong protection out of the box, as long as you use ActiveRecord the way it’s intended and treat raw SQL very carefully.

69. How does the Rails MVC architecture work?

I’d explain it as a separation-of-responsibilities pattern.

In Rails, MVC keeps each part of the app focused on one job:

  • Model, handles data and business rules
  • View, handles what the user sees
  • Controller, handles the request and coordinates everything

A simple way to think about it:

  1. A request hits a controller action, like PostsController#index
  2. The controller talks to the model, for example Post.published
  3. The model fetches or updates data, usually through Active Record
  4. The controller passes that data to a view
  5. The view renders the response, like HTML for a browser or JSON for an API

What each layer does:

  • Models
  • Represent application data
  • Handle validations, associations, scopes, and business logic
  • Talk to the database through Active Record

  • Views

  • Render the final output
  • Usually ERB templates in server-rendered apps
  • In APIs, this might be JSON serializers or templates instead

  • Controllers

  • Receive HTTP requests
  • Read params, call the right model logic, and decide what to render or where to redirect
  • Keep the flow moving, but ideally stay thin

In practice, good Rails code usually means:

  • fat models, thin controllers, when it makes sense
  • views focused on presentation, not business logic
  • controllers acting more like coordinators than decision-makers

So the value of MVC in Rails is that it makes the app easier to read, test, and maintain because data logic, request handling, and presentation are kept separate.

70. How do you handle validations in Rails models?

I keep validations in the model when they represent business rules for that record, especially the stuff that should be true no matter where the data comes from.

My usual approach is:

  • Use Rails built-ins for the common cases
  • presence
  • uniqueness
  • length
  • numericality
  • inclusion
  • format

  • Add conditional validations when rules depend on state

  • For example, validate a field only if a user is an admin, or only on create

  • Use custom validators for anything more domain-specific

  • Things like checking date ranges, preventing invalid status transitions, or enforcing plan limits

  • Keep error messages clear

  • I want the API or UI to be able to show something useful, not just “invalid”

I also try to be practical about where validations belong:

  • Model validations for application-level rules
  • Database constraints for data safety
  • NOT NULL
  • unique indexes
  • foreign keys

That matters because Rails uniqueness validation alone is not enough under concurrency. I’ll usually pair it with a unique index in the database.

A simple example would be:

  • name must be present
  • email must be present and unique
  • age must be a number greater than 18
  • A custom check might make sure end_date is after start_date

If the logic starts getting bulky, I’ll usually extract it into a custom validator or a form/service object instead of stuffing too much into the model. That keeps the model readable and makes the rule easier to test.

Get Interview Coaching from Ruby on Rails Experts

Knowing the questions is just the start. Work with experienced professionals who can help you perfect your answers, improve your presentation, and boost your confidence.

Complete your Ruby on Rails interview preparation

Comprehensive support to help you succeed at every stage of your interview journey

Still not convinced? Don't just take our word for it

We've already delivered 1-on-1 mentorship to thousands of students, professionals, managers and executives. Even better, they've left an average rating of 4.9 out of 5 for our mentors.

Find Ruby on Rails Interview Coaches