Support

Lesson 11 of 11 ·14 min read

App Architectures: Eight Case Studies

How real apps are put together, read through the patterns of the course.

After this lesson you can explain

  • How eight familiar apps, from WhatsApp to a banking app, are remixes of the same eight patterns this course taught.
  • Why an app's identity is the one or two patterns it pushes to the extreme, and why the patterns it deliberately refuses matter just as much.
  • Where everyday UI you already know (WhatsApp's ticks, Instagram's instant heart) maps onto the sync machinery underneath.

The premise

The course taught roughly eight patterns, one at a time. This lesson shows them recombined in apps you use daily, because the secret of app architecture is that there is no secret: every famous app is a remix of the same small pattern set, chosen and weighted differently.

#PatternTaught in
1API client over HTTPS/JSON; server owns truthHow Apps Actually Work
2Token auth: verify server-side, store securely, refreshA Login Flow End to End
3Push via FCM/APNs: notify-then-fetch, token hygienePush Notifications End to End
4Local DB as single source of truth; UI reads localOffline Data & Sync
5Outbox writes: optimistic UI, idempotency, retryOffline Data & Sync
6Delta sync: cursors, tombstones, versioned writesOffline Data & Sync
7Real-time channel: socket/SSE as a low-latency delta feedReal-Time & WebSockets
8Entitlements: receipts/purchases verified server-side into one tablePayments in Apps

Each case study below names its remix. Details of specific companies' stacks are drawn from public engineering write-ups and talks; treat the specifics as illustrative, the shapes as the lesson.

Messaging

1. WhatsApp: the everything-composite

The canonical "uses every pattern at once" app.

Phone A                          Server (relay)                    Phone B
local SQLite = truth               |                          local SQLite = truth
  |                                |                                |
  |-- outbox: encrypt, send ------>|-- store ciphertext ----------->|  (socket if open,
  |<-- ack (one tick) ------------|    cannot read it               |   push if closed)
  |<------------- delivered ack (two ticks) -----------------------|
  |                                |         iOS: notification extension
  |                                |         decrypts push ON-DEVICE
  • Your chat history lives on your phone, in SQLite; the server keeps messages only until delivered. That is why backup/transfer is a whole feature: the local DB really is the single source of truth (pattern 4), unusually strongly.
  • The tick icons are an outbox pattern rendered as UI (pattern 5): clock = queued locally, one tick = server ack, two ticks = delivered ack from the recipient's device. You have been watching idempotent retry semantics your whole life.
  • Socket while open, push while closed (patterns 3 + 7), exactly the split from the real-time lesson.
  • End-to-end encryption (the Signal protocol) means the relay moves ciphertext it cannot read; on iOS, a notification service extension decrypts incoming pushes on-device before display: the rich-push hook from the push lesson doing security work.
  • Engineering folklore: WhatsApp famously served hundreds of millions of users with a tiny team on an Erlang relay core, the payoff of keeping the server a thin, stateless-ish relay and pushing state to the edges.

Remix: 1 + 2 + 3 + 4 (strong) + 5 (visible in UI) + 7. No 6 in the classic design (delivery, not sync); multi-device support forced them to add sync machinery later, a lesson in how features drag patterns in.

Live logistics

2. Uber / Deliveroo: a live dashboard over a dispatch engine

Driver phone ──GPS every ~4s──►  Dispatch backend  ──fan-out──►  Rider phone
                                 (match supply to               (car moves on map)
                                  demand; owns the
Payments: own processor           TRIP STATE MACHINE:
(physical goods, ~3%)             requested → matched → arriving → riding → done
  • Two apps, one state machine. The trip's state lives server-side; driver and rider apps are both just views of it, receiving transitions over their sockets. Neither phone is ever the truth (pattern 1 at its purest).
  • The moving car is pub/sub fan-out (pattern 7): the driver's position stream is published; whichever server holds the rider's socket delivers it. Position updates are also throttled and interpolated client-side; the smooth glide is animation between ~4-second samples.
  • Physical goods economics (pattern 8's classification): rides and food use their own processor at ~3%, which is what makes the category viable in the stores at all.
  • Push carries the doorbell moments ("your driver is arriving", "order delivered") with short TTLs: a stale arrival push is worse than none (the push lesson, TTL section).
  • Offline is deliberately shallow: an ordering app without connectivity is correctly useless; they polish the error states instead (the sync ladder's rung 1, chosen on purpose).

Remix: 1 + 2 + 3 (TTL-heavy) + 7 (the centrepiece) + 8 (physical). Minimal 4/5/6, and that is a design decision, not a gap.

Media + offline

3. Spotify: offline inverted, payments dodged

  • The catalogue breaks the sync ladder's assumption: you cannot hold "all the user's data" when the data is a hundred million tracks. So Spotify inverts: stream from CDN with aggressive prefetch (the next track is already buffered before this one ends; that is the gapless magic), plus selective rung-3 offline where downloads are DRM-encrypted files useless outside the app.
  • Playlists are the actual sync problem (pattern 6): edited on any device, delta-synced through the backend, with the append-mostly data shape keeping conflicts rare (the sync lesson's structural dodge).
  • The subscription is the reader-app pattern (pattern 8): purchased on the web at web economics; the app just asks the entitlements service what this account may do. Premium is a server-side answer, not an app-side flag.
  • Connect (play on your speaker, control from your phone) is pattern 7 wearing headphones: every device holding a socket to the backend, control messages routed between them.

Remix: 1 + 2 + 4 (cache-heavy, not truth-holding) + 6 (playlists) + 7 (Connect) + 8 (reader-app). The clever bit is refusing rung 3 for the catalogue and buying it back selectively.

Feed

4. Instagram: the feed pattern

GET /feed?cursor=...  ──►  ranked page of posts + next_cursor
      images/video from CDN, prefetched as you approach them
tap ♥ ──► UI updates INSTANTLY (optimistic) ──► POST /like queued behind it
  • Cursor pagination as infinite scroll: the feed is pattern 6's delta endpoint experienced as UX. Each page returns items plus an opaque cursor; ranking happens server-side (the order is recomputed per request, which is why refresh reshuffles).
  • Prefetch is the perceived performance: media for the next screenfuls is already downloading from the CDN while you look at this one. The app feels fast because it cheats time, not because requests are fast.
  • The like button is optimistic UI (pattern 5 in miniature): the heart fills instantly from a local write; the POST follows behind with retry. If it ultimately fails, the heart quietly un-fills; you have seen this happen on bad connections.
  • Uploads are an outbox: a post created on the subway shows "posting…" and completes later: queued, retried, idempotent.
  • Media itself never touches the API servers: photos and video live on CDNs; the API deals in metadata and URLs (a pattern worth stealing for any media-heavy app).

Remix: 1 + 2 + 3 (re-engagement heavy) + 5 (likes, uploads) + 6 (the feed itself). The lesson: "the feed" is not a new pattern; it is delta sync plus prefetch plus optimism, styled.

Banking

5. A banking app (Revolut-shaped): the security lesson in a suit

  • Deliberately at the bottom of the offline ladder: balances and transactions are cached for instant display (rung 2), but nothing writes offline. A queued money transfer replaying hours later is a horror story; some operations should require connectivity. Choosing rung 2 here is a safety feature, not a limitation: the sync ladder read backwards.
  • The security checklist deployed in full: certificate pinning (with managed rotation), Play Integrity/App Attest on sensitive endpoints, biometrics unlocking a Keystore-held token (the fingerprint does not "log you in"; it releases the stored credential), blanked app-switcher snapshots, no sensitive data in push payloads (the "You have a new message" pattern), short session lifetimes with silent refresh.
  • The real defences are server-side: transaction risk-scoring, per-device attestation checks, anomaly detection, step-up confirmation for unusual transfers. The app is the polished view; the fraud engine is the product (the security lesson's closing rule at maximum stakes).
  • Real-time is modest and correct: a push on card transactions (doorbell), polling/SSE for balance refresh. No websocket needed; money does not move at chat speed.

Remix: 1 + 2 (hardened) + 3 (notify-only payloads) + 4 (read cache only) + the entire security lesson. Notable for the patterns it refuses: no 5, no offline writes, on purpose.

Collaboration

6. Notion / Google Docs: rung 5, the one you buy

You type "h"  ──►  operation { insert "h" at pos 41 }  ──socket──►  server
                                                                       │ transform/merge against
                                                                       │ everyone else's concurrent ops
   your view <──────────── everyone's operations, converged ───────────┘
   (offline: ops queue locally, replay-and-merge on reconnect)
  • The document is a log of operations, not a file. Every keystroke becomes an op flowing over a websocket; the server (Google Docs popularised operational transforms; newer tools lean on CRDTs) transforms concurrent ops so every client converges on the same result. This is the sync lesson's rung 5 made visible.
  • Presence (the colored cursors) is pub/sub (pattern 7): ephemeral position events fanned out to everyone in the document, with TTLs so crashed clients' ghosts fade.
  • Offline editing works because ops are exactly what an outbox wants to hold (pattern 5): queue locally, replay on reconnect, and the transform machinery doubles as the conflict resolution. This is why the op-log design is chosen: it makes rungs 4, 5, and 7 share one mechanism.
  • The reason the sync lesson said "buy, don't build" is this card: every hard pattern in the course running simultaneously, plus mathematics. Teams of specialists maintain these engines full-time.

Remix: 1 + 2 + 4 + 5 + 6 + 7 fused into a single op-log mechanism: the maximal remix, and the strongest argument for using an existing engine (Liveblocks, Yjs, Automerge) if you ever need it.

Loyalty storefront

7. McDonald's: where this course started

The app that answered "what type of app does a takeaway use" (back in Lesson 1), now readable in full:

  • The business model is pattern 3: push campaigns ("20% off today only") and the loyalty flywheel are the reason the native app exists at all; the ordering could have been a website.
  • Offers and menus are server-driven UI: the app renders what a config API returns. Prices, promos, and layouts change per country and per day with no app release (an antidote to the slow release pipeline from Lesson 2).
  • Payments are the physical-goods carve-out (pattern 8): own processor plus Apple Pay/Google Pay as wallets at card rates. Store distribution with no store tax, the best-of-both-worlds noted back in Lesson 1.
  • Location is the quiet workhorse: store-finder, geofenced offers, and "start cooking when the customer is close" logic: platform services (Lesson 2) doing revenue work.
  • The order flow is a small server-owned state machine (placed → paid → preparing → ready) with polling or push per transition: Uber's architecture at a fraction of the intensity, because the "driver" is a kitchen that does not move.

Remix: 1 + 2 + 3 (the business model) + 8 (physical) + server-driven UI. Modest sync, no real-time sockets needed: a reminder that most successful apps use fewer patterns than engineers expect.

Reader app

8. The Endpoint 51 reader: this site's own book, same patterns

The closing example, deliberately the simplest: the hypothetical companion app for a €35 web-sold book.

App ──magic-link/token auth──►  existing backend  ──►  entitlements: lifetime?
 │                                     │
 │<── chapter content (JSON/HTML) ─────┘        purchase NEVER happens in-app:
 │    cached locally (rung 2)                   Stripe, on the website, ~3%
 └── reading position synced when online        (reader-app pattern)
  • Auth: the existing magic-link flow issuing a token into SecureStore (pattern 2, steps 7–8 only; no OAuth dance needed).
  • Content: chapters fetched over the authenticated API and cached locally: rung 2, with "download for offline" as an optional rung-3 upgrade per chapter, Spotify-style selective offline in miniature (pattern 4).
  • Entitlement: the same table the Stripe webhook already writes; the app just asks "am I paid?" (pattern 8, reader-app variant: no store billing, no 15–30%).
  • Sync: one tiny piece of state worth syncing, reading position, which is last-write-wins-safe because it is one user's single value (the sync lesson's structural dodge, smallest possible case).
  • No push, no sockets, no outbox. A book does not interrupt you. The absence of patterns is the architecture.

Remix: 1 + 2 + 4 (light) + 8 (reader-app). Four patterns, all of which already exist in the website's backend, which is the point.

The lesson underneath the lesson

App1 API2 Auth3 Push4 Local DB5 Outbox6 Delta7 Real-time8 Entitle
WhatsApp●●●●
Uber●●●●
Spotify●●
Instagram●●●●
Banking●●
Notion/Docs●●
McDonald's●●
E51 reader

●● = the app's defining pattern  ·  ● = present  ·  ○ = light/partial  ·  – = deliberately absent

Three closing observations. First, every row is a remix of the same eight columns; no app in this lesson needed a ninth pattern. Second, the double-dots differ: an app's identity is which one or two patterns it pushes to the extreme (WhatsApp its local truth, Uber its fan-out, Spotify its entitlements, banking its auth). Third, the dashes matter as much as the dots: banking refusing offline writes and the reader app refusing push are architecture decisions of exactly the same weight as the features. Reading an unfamiliar app now works the same way: ask which columns it fills, which it doubles down on, and which it deliberately leaves empty.

Check your understanding

  • Pick any app on your phone: which of the eight patterns does it fill, and which one or two does it push to the extreme?
  • Why is a banking app refusing offline writes (or the reader app refusing push) an architecture decision rather than a missing feature?
  • Which familiar piece of WhatsApp UI is the outbox pattern rendered on screen, and what does each tick mean?

Build the server half

Eleven lessons, one recurring shape: the app is a client, and the API underneath is the part you can build today. Mastering APIs With Python teaches exactly that: 30 chapters in Python, from your first request to a deployed, tested service. Three full chapters are free.

Reviewed 4 August 2026.