Ruby on Rails Module 15
Rails internals and extending Rails interview questions
Interview questions on rails internals and extending Rails: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What does the Rails executor do around a request, and when do you call it yourself?
Answered in the lesson Rack middleware, the executor and threads
-
Fresher to mid
What is the difference between nil?, blank? and present? in Rails?
Answered in the lesson Active Support: extensions and notifications
-
Fresher to mid
What does
isolate_namespacechange in an engine, and what breaks without it?Answered in the lesson Engines, generators and application templates
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
Kaksha's new middleware adds x-kaksha-bytes, the body's size, whenever the body responds to to_ary. The header appears on every HTML page but never on the attendance export, which streams its CSV row by row. A teammate proposes calling body.to_a first so that the header is always set. Diagnose the gap and say what to do instead.
- What it tests
- Whether the candidate knows the Rack 3 body contract (each, to_ary, close), why a middleware must not buffer a streamed body, and how to report a size that is known only at the end.
- First step
- Compare the two kinds of body. A Rails view response is complete in memory and responds to to_ary, so its size is known before the headers go out. The export is an Enumerator that builds rows while the server sends them, so no layer can know its size before the last row. Then work out what body.to_a would cost: the whole export in memory before the first byte leaves.
- Follow-ups
-
- Where would you report the size of a streamed body instead?
- What else breaks if the middleware reads the body with each itself?
- Where in the stack should a byte counter sit, and why there?
- How do you test the middleware against both kinds of body?
From the lesson Rack middleware, the executor and threads
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
Under Puma, teachers at one Kaksha centre now and then see another centre's batch list. The batch query is scoped by Centre.current, a class method that memoises the signed-in teacher's centre in @current. Debug the leak, fix it, and say how you would prove the fix with a test that fails before it and passes after.
- What it tests
- Whether the candidate sees that state on a class is shared by every Puma thread, moves per-request data into Current or an argument, knows the executor clears Current, and can force the failing order in a test.
- First step
- Notice that @current on a class is one value for the whole process: the first request fills it and every later request, on any thread, reads it, so the leak needs no unlucky timing. Then move the centre into a Current attribute set from the signed-in teacher in a before_action, or pass it to the query as an argument.
- Follow-ups
-
- Why is Thread.current[:centre] not a fix either?
- How do you write a test that fails on the old code every time?
- Why did the bug show only now and then, and not on every request?
- What changes when a job or a thread you start needs the centre?
From the lesson Rack middleware, the executor and threads
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 4 minutes
-
Mid to senior
After deploying a new monitoring subscriber, the p99 latency of every controller action doubled. The subscriber logs event names to an external service. Explain how you would diagnose this, what the likely cause is, and how you would fix it without removing the monitoring.
- What it tests
- Understanding that subscribers run synchronously, diagnosing with timing, and moving slow work out of the subscriber callback.
- First step
- Check whether the subscriber blocks the request thread. Wrap the subscriber body in a timing call and log the duration. If each event adds tens of milliseconds and an action fires many events, the latency increase is explained. Subscribers run inside instrument, so slow work delays the response.
- Follow-ups
-
- How would you move the logging out of the request cycle?
- Would switching to monotonic_subscribe change the latency?
- How would you test that the subscriber stays fast?
From the lesson Active Support: extensions and notifications
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A multi-tenant application sets the current tenant in ApplicationController and reads it in models using a class variable @@current_tenant. One customer reports seeing another customer's data. What caused this, and how would you fix the models without changing the controller?
- What it tests
- Recognising shared mutable class state, thread-safety of class variables, and using class_attribute or Current attributes for per-request data.
- First step
- Class variables are shared across all threads. When two requests run at once, one thread overwrites the tenant the other set. The fix is to store the tenant in a place that is per-request, such as ActiveSupport::CurrentAttributes, not in a class variable.
- Follow-ups
-
- Why does class_attribute not solve this either?
- How does Current reset between requests?
- How would you test for this race condition?
From the lesson Active Support: extensions and notifications
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
You need to add instrumentation to a fee calculation service so that tests can assert which fee rules were applied and integration tests can measure timing. Design the event naming, payload structure, and show how a test would subscribe, trigger a calculation, and verify the payload.
- What it tests
- Naming events with action.namespace, including testable data in the payload, subscribing in setup, unsubscribing in teardown, and asserting on captured events.
- First step
- Name the event following Rails convention: calculate.fees or apply_rule.fees. The payload should include the student id, the rule that matched, and the calculated amount. In a test, subscribe before the action, capture events in an array, and assert on the payload after the call.
- Follow-ups
-
- How do you avoid the subscriber leaking into other tests?
- How would you add a timing threshold assertion?
- Should the payload include the fee object or just its id?
From the lesson Active Support: extensions and notifications
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to staff
A fees slice of one Rails app is owned by three people, ships in the app's deploy, and a second app will need it next quarter. It also calls two concerns from app/models/concerns. Choose a mountable engine, a packwerk package or a separate service, defend the choice, and say what you would do in the first week.
- What it tests
- Whether the candidate decides from reuse, release schedule and ownership rather than tidiness, and can sequence the work so the boundary is enforced before the second app arrives.
- First step
- Separate the requirements that only one option satisfies. A second app loading the code rules out a package; no independent release and three maintainers rule out a service. That leaves an engine, but an engine is a poor first step while the slice still calls two shared concerns, so say what you would do before the gemspec exists.
- Follow-ups
-
- What do the two shared concerns do to the plan, and how do you deal with them?
- How would you move from a package to an engine later without a big rewrite?
- What would change your answer to a separate service?
- How do you keep the boundary honest in the weeks before the second app arrives?
From the lesson Engines, generators and application templates
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Mid to senior
A deploy stops with "table feature_flags_flags already exists" after the feature_flags engine was upgraded and
bin/rails feature_flags:install:migrationswas run. The table holds live rows. Diagnose the cause from what the app's db/migrate folder and schema_migrations table would show, fix it without losing data, and prevent it.- What it tests
- Whether the candidate reads db:migrate:status and the engine's copied files, knows that install:migrations decides by the class names in db/migrate, and repairs migration history instead of dropping or editing a live table.
- First step
- Run bin/rails db:migrate:status and list db/migrate. Look for an engine copy with the .feature_flags suffix that is down while an older version is up with NO FILE: the task copied again a migration the app had deleted. Its first line names the original version, so confirm it matches the one that created the table.
- Follow-ups
-
- What exactly does install:migrations write into the host app, and what does it skip on a second run?
- Which repair would you choose, and what does it do to the other environments?
- How would a pre-deploy check have caught this?
- What if the upgrade also shipped a genuinely new engine migration?
From the lesson Engines, generators and application templates
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
-
Mid to senior
Design an application template for twelve Rails apps that must also be applied again every quarter as the team's defaults change. Say which Thor actions are safe to repeat and which need a guard, how the template reports what it changed, and how you would test it before it touches twelve apps.
- What it tests
- Whether the candidate knows which Thor actions repeat safely, guards the ones that do not on a stable marker, and can test a template without a repository of live apps.
- First step
- Sort the actions into three groups. Whole-file actions compare content and write nothing on a second run; insertions such as route and insert_into_file skip only their own exact text; gem, generate and run act every time. Say which of your defaults fall in each group, then pick the guard for each of the last two.
- Follow-ups
-
- What guard do you use for an insertion or a command, and where does it read its answer from?
- How do you test the template without twelve real apps?
- What belongs in after_bundle rather than beside the gem call?
- How do you handle a default that must change a file the apps have edited by hand?
From the lesson Engines, generators and application templates
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 5 minutes
All Ruby on Rails interview questions · The Ruby on Rails track