Sheet ⁨09⁩ · ⁨Quizzes⁩Surveyed ⁨2026⁩

Ruby on Rails: Convention Over Configuration

12 QuestionsIntermediatePublished: Updated:

Ruby on Rails: Convention Over Configuration

Up to 20 questions, shuffled on every run

Quiz Configuration
Enterto start

Rails is productive because it decided things for you. The router derives a controller and action from a verb and a path, the controller infers its template from its own name, and Active Record infers table names, column types and primary keys from the schema. You get a lot for very little typing, and the same trade means debugging Rails often requires knowing which convention is quietly in play.

This quiz targets the places where those conventions have sharp edges: the difference between save and save! inside a transaction, why editing a migration that already ran is a bad idea, and how includes turns 101 queries into 2.

Rack middleware, then the router, then callbacks, controller, model and view. The seven routes from a single `resources` line are listed alongside, plus the N+1 pattern and its fix.

Several questions here are really about production behaviour rather than syntax, so it is worth seeing where each Rails piece lands when you deploy it. The web and worker processes come from the same image and scale separately, migrations run as a one-off task rather than in an entrypoint, and Puma’s thread count multiplied by your replica count has to stay under the database connection limit.

Puma behind an ALB, Sidekiq as a separate scalable process, Redis serving both the job queue and Rails.cache, Postgres on RDS with a standby, and Active Storage writing to S3.

Read the explanations even on the questions you get right. Most of them name a specific failure mode rather than restating the correct answer, and those failure modes are the reason the distinction exists.

Answer key and explanations12 questions

The quiz above draws 12 questions at random from these 12, so a second attempt will not be the same run. Everything in the pool is listed here.

  1. In Rails MVC, where does business logic belong?

    AnswerIn the model, or in service objects the model layer owns

    Controllers coordinate: they receive params, invoke domain behaviour, and pick a response. Logic in a controller cannot be reused by a background job, a rake task, or a different endpoint, and it is awkward to test. "Fat model, skinny controller" is the traditional phrasing; on larger apps the model layer often grows into service objects and POROs rather than one enormous class.

  2. What does `resources :articles` generate in `config/routes.rb`?

    AnswerSeven routes: index, new, create, show, edit, update, destroy

    It generates seven routes mapping to index, new, create, show, edit, update and destroy. There are seven actions but only four distinct URL patterns, because show, update and destroy all live at /articles/:id and are distinguished by HTTP verb. Use `only:` or `except:` to generate a subset rather than declaring routes you never implement.

  3. What is the difference between `has_many :through` and `has_and_belongs_to_many`?

    Answer`:through` uses a real model for the join, so the relationship can carry its own attributes

    `has_and_belongs_to_many` uses a plain join table with no model, so the relationship can hold no data of its own. `has_many :through` routes the association through a real model, so the join can have its own columns, validations, callbacks and timestamps. Since almost every many-to-many relationship eventually needs an attribute on the join, `:through` is the safer default.

  4. What does `Article.includes(:author)` do that `Article.all` does not?

    AnswerIt loads authors up front, replacing N+1 queries with a fixed small number

    It eager loads the association, turning the N+1 pattern into a fixed number of queries. Without it, iterating 100 articles and reading `article.author` issues 1 query for articles plus 100 for authors. With it, Rails issues 2 queries and matches the records in memory. Note that `includes` decides between a separate query and a LEFT OUTER JOIN depending on context; use `references` when you need to filter on the joined table.

  5. A migration has been run in production. What is the correct way to change it?

    AnswerWrite a new migration that makes the additional change

    Write a new migration. Rails records applied migrations by timestamp in `schema_migrations`, so editing an already-run migration leaves your database in a state the file no longer describes, and anyone who already ran it never picks up the change. `rails db:rollback` then editing is acceptable locally, before the migration has been shared or deployed, and never after.

  6. What happens when a `before_action` callback calls `redirect_to`?

    AnswerThe chain halts and the controller action is never executed

    The filter chain halts and the action never runs. Any callback that renders or redirects stops the chain, which is exactly the mechanism `authenticate_user!` relies on. If a callback needs to stop processing without rendering, it must call `head :forbidden` or similar, because a callback that neither renders nor redirects lets the action proceed.

  7. What is the difference between `save` and `save!` on an Active Record model?

    Answer`save` returns false on validation failure, `save!` raises an exception

    `save` returns false when validation fails, so you must check the return value or the failure passes silently. `save!` raises `ActiveRecord::RecordInvalid`. The bang versions matter inside transactions and background jobs: an unchecked `save` that fails will let a transaction commit as if everything worked, whereas the raise triggers a rollback.

  8. Which of these Active Record calls skips validations and callbacks?

    Answer`update_column`

    `update_column` writes directly with a single UPDATE, skipping validations, callbacks and the updated_at timestamp. `update` and `update_attribute` differ subtly: `update` runs validations, while `update_attribute` skips validations but still runs callbacks and touches timestamps. These distinctions are a frequent source of bugs where a model reaches an invalid state without any error.

  9. What does `strong parameters` protect against?

    AnswerMass assignment of parameters the developer never intended to expose

    It prevents mass assignment of attributes you did not explicitly permit. Without it, a crafted request containing `user[admin]=true` would set that column if it exists. `params.require(:user).permit(:name, :email)` allows exactly those keys and drops anything else, so adding a sensitive column to a table does not silently make it settable from a form.

  10. In a Rails app, what is the practical difference between `Rails.cache` and a Sidekiq queue backed by the same Redis?

    AnswerCache entries may be evicted; queued jobs must not be, so they need different eviction policies

    A cache is expendable: losing it costs performance, not correctness, so it can use an eviction policy and a memory limit. A job queue is a durable work log, and losing it means work never happens. Sharing one Redis with `maxmemory-policy allkeys-lru` will happily evict queued jobs to make room for cache entries. Use separate instances or at least separate databases with different eviction policies.

  11. What does `rails db:migrate` do that `rails db:schema:load` does not?

    AnswerIt runs each pending migration in sequence, including any data changes inside them

    `db:migrate` runs each pending migration in order, executing your data transformations along the way. `db:schema:load` wipes the database and recreates the structure from `db/schema.rb` in one step, ignoring migrations entirely and running none of their data changes. Use `schema:load` to set up a fresh database quickly, and never on a database with data you want to keep.

  12. Why does `Article.where(published: true).count` differ from `Article.where(published: true).size` in some cases?

    Answer`count` always queries the database, while `size` uses the loaded records if they are already in memory

    `count` always issues a SQL COUNT query. `size` checks whether the relation is already loaded: if it is, it counts the in-memory array without touching the database; if not, it issues a COUNT. So `size` is the better default when you may already have the records, and `count` is what you want when you deliberately need a fresh number without loading rows. `length` always loads the records first.

Was this useful?

You might also enjoy

More posts on similar topics

PHP & Laravel: Modern Web Development Fundamentals

PHP & Laravel: Modern Web Development Fundamentals

Ready to check how well you know PHP and Laravel? This quiz runs through routing and middleware, Eloquent relationships and the N+1 trap, migrations and seeders, Blade, and the artisan workflow. Read

C# & .NET: Language and Runtime Fundamentals

C# & .NET: Language and Runtime Fundamentals

Ready to test how well you know C# and the .NET runtime? This quiz walks through the type system, LINQ, async/await, the CLR and garbage collection, and modern features like records and pattern matchi

Spring Boot: Java Application Framework Essentials

Spring Boot: Java Application Framework Essentials

Spring Boot removed most of the ceremony from Java backend development, but the framework still does a lot of work you cannot see. This quiz walks through dependency injection, auto-configuration, RES

Java: Core Language & JVM Fundamentals

Java: Core Language & JVM Fundamentals

Java has quietly powered banks, Android, and countless backend systems for decades, and the language keeps evolving with records, sealed classes, and virtual threads. This quiz walks through the core

4 related posts