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.
| # | Pattern | Taught in |
|---|---|---|
| 1 | API client over HTTPS/JSON; server owns truth | How Apps Actually Work |
| 2 | Token auth: verify server-side, store securely, refresh | A Login Flow End to End |
| 3 | Push via FCM/APNs: notify-then-fetch, token hygiene | Push Notifications End to End |
| 4 | Local DB as single source of truth; UI reads local | Offline Data & Sync |
| 5 | Outbox writes: optimistic UI, idempotency, retry | Offline Data & Sync |
| 6 | Delta sync: cursors, tombstones, versioned writes | Offline Data & Sync |
| 7 | Real-time channel: socket/SSE as a low-latency delta feed | Real-Time & WebSockets |
| 8 | Entitlements: receipts/purchases verified server-side into one table | Payments 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.
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.
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.
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.
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.
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.
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.
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.
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
| App | 1 API | 2 Auth | 3 Push | 4 Local DB | 5 Outbox | 6 Delta | 7 Real-time | 8 Entitle |
|---|---|---|---|---|---|---|---|---|
| ● | ● | ● | ●● | ●● | ○ | ● | ○ | |
| Uber | ●● | ● | ● | ○ | ○ | ○ | ●● | ● |
| Spotify | ● | ● | ○ | ● | ○ | ● | ● | ●● |
| ● | ● | ●● | ○ | ● | ●● | ○ | ○ | |
| 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.