rails

Targeted Query Caching for Expensive Endpoints

I’ve had endpoints where the same lookups get repeated across helpers and partials, and I didn’t want to pay for query caching on every request. In Scoped query cache, I wrap only the expensive report build in ActiveRecord::Base.cache, so the cache li

Turbo Streams: partial page auth failure handling

When a session expires, Turbo requests can start returning 401/302 and the UI gets confusing. Handle unauthorized turbo requests explicitly: return a stream that updates a “session expired” banner or triggers a redirect.

Composable “Policy Scope” without a Gem

Authorization libraries are great, but you can also build a lightweight policy scope. The key is to keep it composable: a single public method that returns an ActiveRecord::Relation and nothing else.

Parallelize Independent External Calls (in a bounded way)

If you have to hit multiple APIs, you can cut tail latency by running calls concurrently. Keep it bounded and use timeouts. Rails itself is thread-safe for reads; be careful with DB connections and use with_connection for threaded work.

Custom confirm dialog with Stimulus (better than window.confirm)

data-turbo-confirm uses the browser confirm dialog, which is functional but not pretty. For a more polished app, I replace it with a Stimulus controller that intercepts clicks, shows a custom modal, and only proceeds if the user confirms. Turbo makes

Keyboard shortcut “command palette” modal (Hotwire-first)

A command palette feels like a SPA feature, but you can do it Hotwire-first: place a turbo_frame_tag 'modal' in the layout and load the palette HTML into it. A small Stimulus controller listens for meta+k and navigates the modal frame to /palette. The

ActiveRecord::Relation as a Boundary (No Arrays)

Return relations from query objects, not arrays. It keeps composition possible (additional filters, pagination, eager loading) and avoids loading huge result sets accidentally.

Use 303 See Other after POST in Turbo flows

After a POST, Turbo behaves best when you redirect with 303 See Other (Rails symbol :see_other). This avoids the browser trying to re-submit the POST when the user refreshes, and it plays nicely with Turbo Drive’s navigation semantics. I use it especi

Transaction-Safe After-Commit Hook (Avoid Ghost Jobs)

Enqueueing jobs inside a transaction can create “ghost jobs” when the transaction rolls back. Use after_commit or after_create_commit to enqueue work only after the DB commit succeeds.

Avoid Callback Chains: Use Domain Events (In-App)

Callback chains become spooky action at a distance. A simple in-app event bus keeps side effects explicit and testable. This isn’t about Kafka—it’s about clarity and seams.

Attach custom headers to Turbo fetch requests (stimulus-free)

Sometimes you need to attach a header to every Turbo request (like a feature-flag variant, or a client version). Turbo emits turbo:before-fetch-request, which lets you mutate the outgoing request before it is sent. I keep the handler tiny and global (

Audit Trail with JSON Diff (Minimal, Useful)

Auditing isn’t just “save everything”. Capture who did it, what changed, and why. Rails gives you dirty tracking; store diffs in a JSON column. Keep it minimal to avoid ballooning storage.