Turbo Stream form errors: replace only the form frame

Hotwire forms feel “native” when invalid submissions keep you in context. Replace just the form frame with errors and keep the rest of the page intact. Return 422 so clients and caches behave correctly.

Model broadcasts: prepend on create, replace on update

When updates can happen from multiple places, model-level broadcasts keep the UI consistent across tabs without sprinkling stream logic in controllers. Use after-commit hooks so broadcasts only occur once the write is durable.

Turbo Frames: Inline edit that swaps form <-> row

Inline editing works best when the frame boundary is small and stable (one row). Render the display state inside a frame, then link to edit inside the same frame. Submit swaps it back. This keeps controllers simple and UI snappy.

Turbo Streams: Create with prepend + HTML fallback

A good Hotwire endpoint responds to both Turbo and non-Turbo clients. Use respond_to and render a turbo stream that prepends the new record and updates flash/errors, while keeping an HTML fallback for crawlers, redirects, and manual testing.

Keep Controllers Thin: Use Command Objects

Command objects (a.k.a. “actions”) make controllers boring. They’re easy to test, easy to instrument, and they produce a stable API for the rest of your app. This is the kind of structure that makes large Rails apps maintainable.

Safer Background Reindex: slice batches + checkpoints

Full reindexes can be long and fragile. Add checkpoints (last processed id), process in batches, and make it resumable. That turns a scary operation into a routine one.

Safer Deletion with dependent: :restrict_with_error

Sometimes cascading deletes are the wrong UX and the wrong ops story. Restrict deletion when children exist and provide a user-facing error. This prevents data loss accidents.

Transactional Email “Send Once” with Delivered Marker

Emails should be idempotent. Store a delivered marker (or unique key) so retries don’t spam users. This pattern is especially useful for receipts and password reset flows.

Memory-Safe “top tags” aggregation with pluck + group

Tagging systems can be expensive. For quick “top tags” features, compute from the join table with group and count. Avoid loading full taggable records.

“Write Amplification” Guard: Only Update Changed Columns

Avoid writing rows when nothing changed—especially in batch jobs. Check changed? or compute the would-be update and skip if identical. This reduces bloat and autovacuum pressure.

Defensive Deserialization for ActiveJob

Deploys happen while jobs are in the queue. Be defensive: accept both old and new payload shapes, and keep migrations forward-compatible. This prevents “deploy broke jobs” incidents.

Deadlock-Aware Retry Wrapper

Deadlocks happen under load. When the operation is safe to retry, rescue the deadlock exception and retry with jitter. Keep retries bounded and log when it happens.