During standup, a new Elixir dev asks what join/3 does in a Phoenix Channel. What's the correct explanation?
join/3 receives the topic string, the join payload from the client, and the socket. Returning {:ok, socket} admits the client; returning {:error, %{reason: "unauthorised"}} rejects it. This is the right place to verify auth tokens, load user data into socket.assigns, and set up initial channel state.
2 / 5
In a PR review, a colleague questions your Phoenix Channel topic naming convention. How should topics be structured?
Phoenix uses a "namespace:id" convention. In the socket router you write channel "room:*", RoomChannel — the * wildcard matches any room id. A single RoomChannel module handles all room subscriptions; the specific id is available as the topic in join/3.
3 / 5
An incident shows messages being duplicated for senders. The culprit is broadcast vs broadcast_from. What's the difference?
broadcast_from/3 excludes the originating socket from receiving the message — essential for chat where the client optimistically displays its own message. broadcast/3 sends to everyone including the sender, causing duplication if the client also renders sent messages locally.
4 / 5
In a design review, the team asks about intercept and handle_out in Phoenix Channels. What do they enable?
By declaring intercept ["new_message"] and implementing handle_out/3, you intercept broadcasts before delivery. Each socket's handle_out runs independently, so you can filter fields (e.g. hide admin-only fields based on socket.assigns.role) or even drop the event for specific sockets.
5 / 5
During a code review, a teammate confuses channel assigns vs socket assigns. What's the distinction?
Socket assigns (set in UserSocket.connect/3) persist for the lifetime of the WebSocket connection and are available in all channels as the initial assigns. Channel assigns are set inside join/3 or handle_in/3 and are scoped to that specific channel subscription — changes in one channel don't affect another.