Where the multiplication happens
Group chat turns one send into many deliveries, and your design question is where that multiplication happens.
Take a 200-member group. The sender's server writes once to the group conversation and publishes one event, each member's connection server picks it up and pushes to its local sockets, and offline members catch up through their cursors.
See why the storage model matters here: the message is stored once, not 200 times.
The messaging apps cap group size, and partly for this reason. Receipts and delivery bookkeeping scale with your membership, not with your messages.
Model receipts as a little state machine. Sent means your server saved it, delivered means a recipient device acknowledged it, read means the conversation was on screen.
Push each transition back through the same channel, and watch the bookkeeping get nearly quadratic. A read receipt in a 200-member group is 200 tiny events per message per reader.
Aggregate them into counts rather than a row per person, at least until somebody opens the details panel, or your receipts will out-write your messages.
Presence, the classic trap
Treat presence as the classic scale trap it is, because it looks trivial. Broadcast every online and offline flip to all contacts and you melt the moment a phone on a flaky train connection flaps every few seconds.
Use heartbeats with lazy expiry instead. Your client pings every 5 seconds, your server sets a key with a 10 second expiry, and presence reads check that key on demand.
Broadcast only debounced transitions, and only to people currently looking at a screen where that presence appears. Nobody needs to know within a second that a contact they have not opened in months went offline.
Worked example
Fatima owns presence at a workplace chat product with 3 million concurrent users. The v1 design publishes every status change to every teammate, and one Monday a mobile carrier hiccup makes 200,000 phones reconnect repeatedly for 10 minutes. Each flap fans out to an average of 40 coworkers, and the pub/sub tier peaks at 900,000 presence events per second, starving actual messages, which queue up 20 seconds behind. Messages beaten by green dots is the incident title. Her redesign: clients heartbeat every 5 seconds into Redis keys with 10 second TTLs, transitions are debounced for 30 seconds before broadcast, and clients subscribe to presence only for the roster currently rendered on screen. The same carrier flap a month later produces 4,000 events per second and no user-visible impact.