case study 03
01 / 06
context
Ping started as a client for a third-party chat API I did not control. I documented its contract and quirks: the send acknowledgement carries no message or id, the sender never receives its own message event, and order comes from timestamps. Then I wrote my own backend to the same contract and closed the delivery gaps.
decisions
- 01Additive changes behind a capability handshake, not a breaking v2. The server announces its features on connect (
server:hello). The client turns on delivery mode only when it sees them, so it still works against the original hosted API, and old clients still work against the new server. - 02Idempotent sends with a client-generated key. Each send carries a
clientMessageId; a unique index on(sender, clientMessageId)makes a retry return the stored message instead of creating a duplicate. That also makes it safe to fall back to REST when a socket acknowledgement is lost. - 03Server-assigned order, not clocks. Each conversation has a counter incremented atomically in MongoDB; every message gets a
seq. Gaps are allowed (a lost race burns a number), soseqmeans order, never count. - 04Replay instead of refetch. On reconnect the client sends the highest
seqit holds per conversation and receives only what it missed. - 05Query invalidation over optimistic updates (Kept from the client work.) Optimistic updates feel faster but show the user something the server may reject.
evidence
- 01Integration tests on in-memory MongoDB: a duplicate send (sequential or concurrent) stores one message; concurrent sends from two users get unique ordered
seqs; a forced disconnect then reconnect and sync gives no gaps and no duplicates; pagination boundary cases; the backfill migration is idempotent. - 02Full reasoning:
docs/delivery.md. writeup
limits
- 01Single server instance. Running two would need the Socket.io Redis adapter; ordering already lives in the database, so it would hold.
- 02A sender's other tabs don't receive their own messages live; they catch up on the next sync.
- 03No read or delivery receipts.
- 04Sign-in is a sandbox: any phone number, no verification.
stack
- Next.js
- TypeScript
- TanStack Query
- Express
- Socket.io
- MongoDB





