# Action Cable and WebSockets in Rails: Complete Guide for Technical Interviews 2026 > Deep dive into Action Cable and WebSockets in Ruby on Rails. Covers connections, channels, broadcasting, Solid Cable in Rails 8, scaling with Redis, and common interview questions with code examples. - Published: 2026-05-07 - Updated: 2026-05-07 - Author: SharpSkill - Tags: ruby on rails, action cable, websockets, real-time, solid cable, rails 8 - Reading time: 11 min --- Action Cable brings WebSocket support directly into Rails, enabling real-time features like chat, notifications, and live dashboards without external dependencies. For technical interviews in 2026, understanding Action Cable internals — from connection lifecycle to production scaling — separates strong candidates from the rest. > **Key Takeaway for Interviews** > > Action Cable integrates WebSockets into the Rails framework with the same conventions used for controllers and models. Rails 8 introduces Solid Cable, a database-backed adapter that eliminates the Redis requirement for pub/sub messaging. ## WebSocket Protocol Fundamentals in the Rails Context The WebSocket protocol (RFC 6455) establishes a persistent, full-duplex communication channel over a single TCP connection. Unlike HTTP request-response cycles, WebSockets maintain an open connection where both client and server can send messages at any time. Action Cable wraps this protocol into a Rails-friendly abstraction. The server handles WebSocket upgrades at `/cable`, manages connection authentication, and routes messages through channels. The client-side JavaScript library creates and manages subscriptions automatically. A typical HTTP request completes in milliseconds and closes. A WebSocket connection stays open for minutes, hours, or the entire session duration. This fundamental difference drives every architectural decision in Action Cable. ## Action Cable Architecture: Connections, Channels, and Subscriptions Action Cable follows a layered architecture with three core abstractions: connections handle authentication, channels encapsulate business logic, and subscriptions link consumers to specific channels. ```ruby # app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user end private def find_verified_user # Cookies are available in WebSocket handshake if verified_user = User.find_by(id: cookies.encrypted[:user_id]) verified_user else reject_unauthorized_connection end end end end ``` The connection class runs once per WebSocket handshake. Authentication happens here — not in individual channels. The `identified_by` declaration registers the user identity, making it available across all channel subscriptions on that connection. ```ruby # app/channels/chat_channel.rb class ChatChannel < ApplicationCable::Channel def subscribed room = ChatRoom.find(params[:room_id]) # Authorization: verify user belongs to this room if room.members.include?(current_user) stream_for room else reject end end def receive(data) message = ChatMessage.create!( room_id: params[:room_id], user: current_user, body: data["body"] ) ChatChannel.broadcast_to( message.room, { id: message.id, body: message.body, user: current_user.name } ) end def unsubscribed # Cleanup: mark user as offline AppearanceTracker.mark_offline(current_user) end end ``` Channels define three lifecycle callbacks: `subscribed`, `receive`, and `unsubscribed`. The `stream_for` method binds the subscription to a specific model instance, creating a namespaced stream. Broadcasting to that stream delivers messages to every connected subscriber. ## Client-Side Subscriptions with JavaScript The client-side consumer connects to the Action Cable server and manages subscriptions. Each subscription maps to a server-side channel. ```javascript // app/javascript/channels/chat_channel.js import consumer from "./consumer" const chatChannel = consumer.subscriptions.create( { channel: "ChatChannel", room_id: roomId }, { connected() { // Called when the subscription is ready console.log("Connected to chat room", roomId) }, disconnected() { // Called when the subscription is closed console.log("Disconnected from chat room") }, received(data) { // Called when data is broadcast to the channel const messageList = document.getElementById("messages") messageList.insertAdjacentHTML("beforeend", `
` ) }, sendMessage(body) { // Calls ChatChannel#receive on the server this.perform("receive", { body: body }) } } ) ``` The `received` callback fires every time the server broadcasts to the subscribed stream. The `perform` method sends data from client to server, invoking the corresponding channel method. ## Solid Cable in Rails 8: Database-Backed Pub/Sub Rails 8 introduces Solid Cable as part of the "Solid Trifecta" alongside Solid Queue and Solid Cache. Solid Cable replaces Redis as the pub/sub backend by storing messages in a database table. ```yaml # config/cable.yml (Rails 8 default) development: adapter: solid_cable production: adapter: solid_cable connects_to: database: writing: cable polling_interval: 0.1.seconds message_retention: 1.day ``` Solid Cable works by writing each broadcast message to a database table and polling for new messages at a configurable interval. The default polling interval of 100ms provides near-real-time delivery for most applications. The tradeoff is clear: Solid Cable eliminates an infrastructure dependency (Redis) at the cost of slightly higher latency and database load. For applications already running PostgreSQL or MySQL, this tradeoff often makes sense. For high-frequency broadcasting (thousands of messages per second), Redis remains the better choice. ```ruby # config/database.yml (Rails 8 multi-database) production: primary: <<: *default database: myapp_production cable: <<: *default database: myapp_cable_production migrations_paths: db/cable_migrate ``` Solid Cable uses a dedicated database to avoid contention with the primary database. This separation keeps message polling from interfering with application queries. ## Broadcasting Patterns and Server-Side Triggers Broadcasting from models, jobs, and controllers covers the three most common patterns in production Rails applications. ```ruby # Broadcasting from a model callback class Notification < ApplicationRecord belongs_to :user after_create_commit :broadcast_to_user private def broadcast_to_user ActionCable.server.broadcast( "notifications_#{user_id}", { id: id, title: title, read: false } ) end end # Broadcasting from a background job class DashboardUpdateJob < ApplicationJob queue_as :default def perform(dashboard_id) dashboard = Dashboard.find(dashboard_id) stats = dashboard.compute_stats ActionCable.server.broadcast( "dashboard_#{dashboard_id}", { stats: stats, updated_at: Time.current.iso8601 } ) end end ``` Model callbacks suit simple notifications triggered by record changes. Background jobs handle heavier computations — computing dashboard stats or aggregating data — without blocking the request cycle. The broadcast itself is always lightweight: it serializes the payload and publishes to the adapter. ## Turbo Streams and Action Cable Integration Rails 8 with Hotwire uses Action Cable as the transport layer for Turbo Streams, enabling real-time DOM updates without writing custom JavaScript. ```ruby # app/models/message.rb class Message < ApplicationRecord belongs_to :chat_room # Automatically broadcasts append to subscribers broadcasts_to :chat_room end # app/views/chat_rooms/show.html.erb <%= turbo_stream_from @chat_room %>