Functional requirements
Pick a small core. A safe set:
- Send and receive 1:1 text messages
- Delivery and read receipts (sent → delivered → read)
- Presence (online / last seen / typing)
- Group messaging (small groups, up to ~256 members)
- Send media (images, voice notes)
Five flows is enough to force every interesting tradeoff: persistent connections vs. polling, push delivery vs. pull, ephemeral state vs. durable history, fanout to N recipients, and decoupling bytes from messages. Explicitly defer: end-to-end encryption, multi-device sync, voice/video calls, status (stories), payments, search, communities/broadcast at huge scale, message reactions, replies/quotes.
Non-functional requirements
These shape the architecture more than the features do.
- Low-latency push: a sent message reaches an online recipient in well under a second.
- Durability: a message accepted by the server is never lost, even if the recipient is offline for days.
- Availability over consistency for receipts and presence: it’s fine if the second tick takes a beat to flip; outages are not.
- Connection-heavy: most clients hold a long-lived connection open, online or backgrounded.
Core entities
Before sizing or APIs, name the objects you’re modeling. Fields and storage come later:
- User — the account: id, phone number, profile.
- Conversation — a 1:1 thread or a group, identified by a stable id.
- Message — a single post in a conversation: id, sender, body or media pointer, timestamp.
- MessageStatus — per-recipient state: sent, delivered, read.
- Group — a conversation with multiple members and metadata (name, admins).
- Presence — derived, ephemeral: a user’s current online state and last-seen timestamp.
Presence and MessageStatus aren’t stored the same way as messages — one is in-memory with a TTL, the other is a per-(message, recipient) row that mutates a few times. Calling them out now is what makes the presence chapter (6) and receipts chapter (5) meaningful later.
Core entities at a glance
flowchart TD User(["User"]) Conv(["Conversation"]) Msg(["Message"]) Status(["MessageStatus (per recipient)"]) Group(["Group"]) Pres(["Presence (ephemeral)"]) User -->|"sends"| Msg Msg -->|"belongs to"| Conv Group -->|"is a"| Conv Msg -->|"has"| Status User -->|"member of"| Group User -->|"has"| Pres