Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Low-Level Design (Object-Oriented Design)  Module 15 – Case studies: platforms and apps

Low-level design: chat application

Design a chat application in code: sequence numbers per conversation, client message ids for retries, receipts that only move forward, and group rooms.

  • Intermediate
  • 40 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7 and Node.js 24.21.0
  • By MySmartCoPilot
Free intro, Pro: 10 of 40 minutes free
Free: The prompt, the requirements and the first design. With a pass: the other 12 sections and 3 interview answers, about 30 minutes.

What you will learn

  • Model users, conversations, messages and delivery and read receipts, with one owner for each rule
  • Order messages per conversation with sequence numbers, and turn a retried send into one message
  • Design a group room as a Mediator that owns membership, muting and posting rules
  • Test a phone's view of a conversation against a seeded network that loses, repeats and delays pushes
  • Compare reconnect policies after a server restart, with numbers

Before you start

What you’ll learn · Before you start

What you will learn

  • Model users, conversations, messages and delivery and read receipts, with one owner for each rule
  • Order messages per conversation with sequence numbers, and turn a retried send into one message
  • Design a group room as a Mediator that owns membership, muting and posting rules
  • Test a phone's view of a conversation against a seeded network that loses, repeats and delays pushes
  • Compare reconnect policies after a server restart, with numbers

Before you start

On this page

In a low-level design (LLD) round, “design a chat application” means the classes that store each message once, show every conversation in the same order on every phone, tell the sender whether a message arrived and was read, and run group rooms by their own rules. Four decisions carry this design. The server gives each message the next sequence number of its conversation, and phones order by that number, never by a clock. The sender’s phone picks a client message id, so a send retried after a lost reply stores nothing new. Receipts are records of their own: two marks per member that only move forward. And a group room is the Mediator between its members, so who may post, who receives a message and who is alerted are its rules alone.

The prompt, the requirements and the first design

The prompt, “design a chat application”, hides its hard parts, so the first minutes go on questions. Groups too, and how large? Do senders see delivered and read marks, and in a group, read by how many? What happens to messages sent while a phone is offline, and to a send repeated when the network drops? The answers assumed here:

  • C1 A conversation is a direct chat between two people or a group room of up to 256 members.
  • C2 Each message gets the next sequence number of its conversation, and every phone shows that order.
  • C3 The sender’s phone gives each message a client id; a retried send stores nothing and returns the first number.
  • C4 The sender sees whether a message reached each member’s phone and whether the member read it; neither moves back.
  • C5 Admins add and remove members; a muted member receives without an alert; a new member sees messages from the moment they joined; an admin may let only admins post.
  • C6 A phone that was offline or missed a push asks for everything after the last message it has.
  • N1 The model reads no real clock and opens no socket: the time and the transport are given to it.

Out of scope, said aloud: encryption, attachments, typing indicators, alerts to offline phones, and several phones per person.

C2 deserves the first test, because the obvious designs fail it. Three friends plan dinner. Ben’s phone clock runs 90 seconds slow, Asha’s third message is sent twice because its reply was lost, and the network brings message 4 to Chen’s phone before message 3:

Chen's phone shows one conversation three ways Python · arrival/arrival_order.py
"""Three friends plan dinner in a group chat, and Chen's phone shows the conversation three ways.

Ben's phone clock runs 90 seconds slow. Asha's third request reaches the server, but its reply is lost on her weak
connection, so her phone sends it again five seconds later. The network then delivers message 4 to Chen's phone
before message 3. Times are milliseconds on the server's clock; nothing here reads a real clock.
"""

SENDS = [  # (server time, sender, the sender's own message number, text)
    (0, "Asha", "a1", "Are we still on for seven?"),
    (4_000, "Ben", "b1", "Yes. I booked a table for four."),
    (9_000, "Asha", "a2", "Running ten minutes late."),
    (11_000, "Ben", "b2", "No problem, we will order starters."),
    (14_000, "Asha", "a2", "Running ten minutes late."),  # the retry: same message, same number
]
PHONE_CLOCK_ERROR = {"Asha": 0, "Ben": -90_000}  # Ben's phone is 90 s slow
DELAY_TO_CHEN = [300, 200, 3_500, 300, 300]  # one slow path for the third request


def store(dedupe: bool) -> list[dict]:
    """The server's log. Each stored message gets the next number of the conversation."""
    log, seen = [], set()
    for request, (at, sender, client_id, text) in enumerate(SENDS):
        if dedupe and (sender, client_id) in seen:
            continue  # a retry: the message is already stored, under its number
        seen.add((sender, client_id))
        log.append({"seq": len(log) + 1, "sender": sender, "text": text, "request": request, "at": at,
                    "phone_time": at + PHONE_CLOCK_ERROR[sender]})
    return log


def deliveries(log: list[dict]) -> list[dict]:
    """The pushes to Chen's phone, in the order they arrive."""
    return sorted(log, key=lambda m: m["at"] + DELAY_TO_CHEN[m["request"]])


def show(title: str, messages: list[dict]) -> None:
    print(title)
    for m in messages:
        print(f"   {m['sender']:<5} {m['text']}")


every_request = store(dedupe=False)
show("1. Every request stored, shown in the order the pushes arrive", deliveries(every_request))
show("2. Every request stored, sorted by the time on each sender's phone", sorted(every_request, key=lambda m: m["phone_time"]))

numbered = store(dedupe=True)
shown, early = [], {}
for m in deliveries(numbered):
    early[m["seq"]] = m
    while len(shown) + 1 in early:  # show a message only when every earlier number is here
        shown.append(early.pop(len(shown) + 1))
show("3. One message per (sender, number), shown in the conversation's own order", shown)
print(f"The server stored {len(every_request)} messages without the senders' numbers, {len(numbered)} with them.")

Output

1. Every request stored, shown in the order the pushes arrive
   Asha  Are we still on for seven?
   Ben   Yes. I booked a table for four.
   Ben   No problem, we will order starters.
   Asha  Running ten minutes late.
   Asha  Running ten minutes late.
2. Every request stored, sorted by the time on each sender's phone
   Ben   Yes. I booked a table for four.
   Ben   No problem, we will order starters.
   Asha  Are we still on for seven?
   Asha  Running ten minutes late.
   Asha  Running ten minutes late.
3. One message per (sender, number), shown in the conversation's own order
   Asha  Are we still on for seven?
   Ben   Yes. I booked a table for four.
   Asha  Running ten minutes late.
   Ben   No problem, we will order starters.
The server stored 5 messages without the senders' numbers, 4 with them.

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 arrival_order.py

Showing pushes as they arrive puts Ben’s answer before Asha’s apology. Sorting by each sender’s phone clock puts both of Ben’s messages before the question they answer. Both views show the apology twice. Only the third view is the same on every phone, because one counter per conversation sets the order and the retry carried the same client id.

Class diagram: ChatService owns Conversations and uses a Transport port; a GroupRoom is a Conversation; members and messages belong to one.ChatService+send(conv, sender, client_id, body)seq+delivered(user, conv, up_to)+read(user, conv, up_to)+sync(user, conv, after_seq)list[Message]Conversation+idstr+messageslist[Message]+by_clientdict[(sender, client_id), Message]+post(sender, client_id, body, at)(Message, new)+recipients(message)list[(user, alert)]+mark(user, delivered, read)+status(seq)(delivered, read, out of)GroupRoom+admins, muted+announcements_onlybool+add(by, user)+remove(by, user)+mute(user)Member+userstr+joined_afterseq+deliveredseq+readseqMessage+seqint+senderstr+client_idstr+bodystr«interface»Transport+push(user, message, alert)WebSocketServer+push()FakeNetwork+push()owns*uses112..*1*

The chat application's class diagram

Text description of the diagram

The diagram shows eight classes in UML notation, each box naming a class, then its attributes and, below a line, its operations.

  • ChatService has four operations: send(conv, sender, client_id, body), which returns a seq; delivered(user, conv, up_to); read(user, conv, up_to); and sync(user, conv, after_seq), which returns a list of messages.
  • ChatService owns any number of Conversations and uses exactly one Transport.
  • A Conversation has an id, its list of messages and a map from (sender, client_id) to the message that pair made. Its operations are post(sender, client_id, body, at), which returns the message and whether it is new; recipients(message), which returns each recipient with whether to alert them; mark(user, delivered, read); and status(seq), which returns how many members it reached, how many read it, and out of how many.
  • A Conversation is composed of two or more Members and any number of Messages, shown by filled diamonds at the Conversation end.
  • A Member has a user, joined_after (the last seq before they joined), and two marks: delivered and read, each a seq.
  • A Message has a seq, a sender, a client_id and a body.
  • GroupRoom is a specialisation of Conversation, drawn with a hollow triangle pointing at Conversation. It adds admins and muted members, an announcements_only flag, and the operations add(by, user), remove(by, user) and mute(user).
  • Transport is an interface with one operation, push(user, message, alert). Two classes realise it, drawn with dashed lines and hollow triangles: WebSocketServer, used in production, and FakeNetwork, used in tests.

The service owns the conversations and hands each new message to a transport port. Six choices shape the design:

  • Sequence numbers per conversation: the conversation counts its own messages, so no clock decides the order.
  • Client message ids: a sender and a client id name one message, so a retry finds it.
  • Cumulative receipts: a phone reports “everything up to 41 arrived”, and read marks only grow.
  • The room as Mediator: members never reach each other; the room decides who receives and who is alerted.
  • Transport behind a port: a WebSocket server and a fake network implement one interface.
  • Jittered reconnects: after a restart, phones wait a random time before reconnecting, so they arrive spread out.

The deep dives that decide whether this design holds:

  1. Why one counter per conversation, and who keeps it when there are several servers?
  2. Why look up the client id before any other rule?
  3. How do receipts for a group of 200 stay small, and how is “read by 37 of 199” counted?
  4. Where do mute, join and posting rules live, and how does a new rule arrive?
  5. What does a phone do with copies, late pushes and gaps, and when does it ask the server?
  6. What happens when a server restarts and 10,000 phones reconnect at once?

An answer is judged on the requirements, ordering and retries, receipts, the room’s rules, and failure handling.

Sending once: one counter per conversation, one message per client id Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A group room is the Mediator between its members
  • Receipts as two marks per member, with the numbers
  • A flaky network, measured: copies, late pushes and gaps
  • Reconnects after a restart, with jittered backoff measured
  • Failure drills: four ways a chat goes wrong
  • Designs this one rejects
  • What each level is expected to cover
  • A 45-minute self-mock
  • Practise: edits and deletes, with history and per-member views
    • Message edits and deletes, with history and per-member views
  • Check yourself
  • Interview questions
    • Why does a chat application order messages by a sequence number per conversation rather than by timestamps?
    • Users report that a message sometimes appears twice for everyone in a group, usually when the sender's phone switches from Wi-Fi to mobile data while sending. Explain how this happens, then design the fix on the phone and on the server.
    • A group of 200 shows "read by N" under every message, and its receipts table grows by about two million rows a day. Redesign receipts so that storage stays small and the counts stay correct. What do you give up?
    • A chat server holding 50,000 connections restarts. For a minute the new server is overloaded, and afterwards some users report missing messages from around the restart. Explain both problems and design what the phones and the server should do.
  • Key takeaways

1 diagram · 4 runnable examples (Python, chat.py, devices.py, flaky_network.py, group_evening.py) · 1 exercise with sample tests · 3 interview answers · about 29 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; nothing renews unless you choose to. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Read a complete sample first: Low-level design: parking lot (the model)

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.