Skip to content

Instantly share code, notes, and snippets.

@decagondev
Created July 9, 2026 18:37
Show Gist options
  • Select an option

  • Save decagondev/a67697857ced42f0a12a177f270a72f5 to your computer and use it in GitHub Desktop.

Select an option

Save decagondev/a67697857ced42f0a12a177f270a72f5 to your computer and use it in GitHub Desktop.

Module 3 Challenger — Take-Home

Distributed State & Consistency: Placing Each Operation on the Spectrum · the spectrum

On one machine you chose a representation. Across many, you lose the luxury of having the truth and the speed at once. This take-home asks you to take a real application and place its operations on the consistency spectrum — each at the weakest guarantee it can correctly tolerate, and not a rung stronger. The grade is in showing that different operations need different rungs, and naming what each one costs.

Time: ~45–60 min · Due: before Module 4 · Submit: post your write-up in the cohort channel.


The assignment

Part A — Place three operations, justify each (core)

Pick one application — a ride-share backend, a messaging app, an online store — and choose three core operations that genuinely differ in what they can tolerate.

⚠️ Two systems are off-limits as your submission: the collaborative document editor placed below (it's the worked demo), and the retail bank / cash machine we placed together in class. Pick something else.

In a ~300–400 word write-up, place each operation on the spectrum:

  1. The weakest guarantee it can correctly tolerate — eventual, causal, linearizable, or external. Weakest-tolerable, not strongest-available.
  2. What it promises on concurrent access, and what during a partition. Two questions, asked of every store.
  3. The backing system (or a modern descendant) that provides that rung.
  4. The cost you pay for that rung — latency, coordination, or tolerated staleness. Name the price; a guarantee with no visible cost just means you haven't found it yet.

Part B — Three reflections (a paragraph each)

  1. A time you reached for eventual consistency on something that couldn't tolerate a stale read — or the reverse, paying for strong consistency something didn't need. What broke, or what did you overpay?
  2. A guarantee whose cost you can now name precisely — "this cost me exactly this."
  3. Where predict-then-reconcile shows up in something you've built or used — optimistic UI, collaborative editing, a game.

Optional — Peer response (this is our discussion, async)

Reply to one classmate's audit. Find an operation placed at the wrong rung — paying for strong consistency it doesn't need, or tolerating staleness it can't afford — and propose the right placement with its cost.


Before you start: the moves you're applying

Everything below is just these, applied to one application. Keep them in front of you:

  1. You can't have the truth and the speed at once. It's a standing condition, not a bug. The question is never "how do I avoid it" but "which do I give up, where."
  2. Place each operation at its weakest tolerable rung. A like-count tolerates a stale read; a balance does not. Same app, different rungs — that's the whole skill.
  3. A cost buys a guarantee. You pay with a restriction (MapReduce's pure functions) or with latency (Spanner's commit-wait). Name the price every time.
  4. Predict, then reconcile when you can't wait for the truth. Authoritative anchor + optimistic local action + reconciliation — invisible when right, graceful when wrong.

A worked starter — placing operations in a different system

Let's run the placement on something that is not your assignment: a collaborative document editor — many people editing one shared doc at once. Three operations, three rungs. Watch the moves, then make them on your own system.

Operation 1 · Edit the document text. Two people type in different paragraphs simultaneously. Does this need a single global order? No — it needs convergence: everyone ends up at the same document, and edits that didn't causally depend on each other can merge in any order. So this sits at causal / eventual (this is exactly what CRDTs and OT are for). On concurrent access: edits merge. During a partition: you keep editing offline and reconcile on reconnect. The cost: merge complexity, and occasionally a surprising-but-consistent merge result. This is predict-then-reconcile in the open — type now, reconcile when peers' edits arrive.

Operation 2 · Change sharing permissions. Revoke someone's access. Can this be stale? Absolutely not — a stale read here means a removed collaborator keeps editing a document they've been locked out of. This wants linearizable consistency: the revocation takes effect in a single, unambiguous order, visible to all. On concurrent access: serialised. During a partition: I'd rather deny than risk a stale grant (fail closed — a taste of Module 5). The cost: latency on every permission check, and possibly unavailability during a partition. Worth it — the cost of a wrong answer is unbounded.

Operation 3 · Show presence (who's online, cursor positions). A cursor a second out of date is fine; a name lingering in the sidebar for two seconds after someone leaves is fine. Availability beats accuracy here. Eventual consistency. On concurrent access: best-effort. During a partition: show stale presence rather than nothing. The cost: visible staleness — and it costs nothing that matters.

Notice what just happened: one app, three operations, three different rungs — eventual for presence, causal for text, linearizable for permissions — each justified by what it can tolerate, each with its cost named. Choosing one consistency level for the whole editor would have over-paid for presence or under-served permissions. That's the whole of Part A. Your job is to make those moves on your system.


The shape you're auditing

One application, three operations, placed at three points on a ladder where cost rises as you climb. Your audit puts your operations on this ladder and names the bill at each rung.

flowchart TD
    EVENT["EVENTUAL<br/>presence / cursors<br/>cost: visible staleness"]
    CAUSAL["CAUSAL<br/>document edits<br/>cost: merge complexity"]
    LIN["LINEARIZABLE<br/>permission changes<br/>cost: latency · maybe unavailable"]
    EXT["EXTERNAL<br/>(global order)<br/>cost: latency you wait out"]

    EVENT -- "+ latency, + order" --> CAUSAL
    CAUSAL -- "+ latency, + order" --> LIN
    LIN -- "+ latency, + order" --> EXT

    style EVENT stroke:#6FCF97,stroke-width:1.5px
    style CAUSAL stroke:#FFB938,stroke-width:1.5px
    style LIN stroke:#F5A623,stroke-width:2px
    style EXT stroke:#C97E12,stroke-width:1.5px
Loading

Your turn — a template to fill in

Copy this into your doc and fill one block per operation. Keep it tight — most of the marks are in justifying the rung and naming the cost.

APPLICATION (one line): ___________________________________

OPERATION 1: ______________________________________________
   Weakest tolerable rung: _________________________________
   On concurrent access / during a partition: ______________
   Backing system: _________________________________________
   Cost paid for this rung: ________________________________

OPERATION 2: ______________________________________________
   Weakest tolerable rung: _________________________________
   On concurrent access / during a partition: ______________
   Backing system: _________________________________________
   Cost paid for this rung: ________________________________

OPERATION 3: ______________________________________________
   Weakest tolerable rung: _________________________________
   On concurrent access / during a partition: ______________
   Backing system: _________________________________________
   Cost paid for this rung: ________________________________

If you want a diagram, copy the Mermaid ladder above and place your three operations on it — it renders as a flowchart in your Gist.

Part B — starter prompts (answer in your own words; don't just restate these):

  1. Find a real bug or near-miss: a stale read that mattered, or a strong guarantee you paid for and never needed. What was the operation's true tolerance?
  2. Pick one guarantee in something you've shipped and finish the sentence precisely: "this consistency level cost me exactly ______." If you can't finish it, the cost is still hiding.
  3. Spot predict-then-reconcile in the wild — the like that turns blue before the server confirms, the doc that lets you type offline. What's the authoritative anchor, and how graceful is the reconciliation when the prediction was wrong?

Common traps (self-check before you submit)

  • One rung for the whole app. "We use a strongly-consistent database" is not an analysis — it's the over-paying-everywhere failure. Different operations, different rungs.
  • Strongest-available instead of weakest-tolerable. "Linearizable to be safe" ignores the operation. Safe isn't the goal; correctly tolerable is.
  • Default-eventual on something that can't be stale. The mirror error. A balance, a permission, an inventory count — these break on a stale read.
  • A guarantee with no named cost. If a rung looks free, you haven't found its price. Latency, coordination, or staleness — one of them is the bill.
  • Confusing "during a partition" with "on concurrent access." They're different questions. Answer both for each operation.

What a strong submission shows

  • Places three operations at three different rungs — and shows why each differs.
  • Justifies each by what it can correctly tolerate, not by a default or by "safety."
  • Names the backing system and the cost for every rung.
  • Never buys a guarantee stronger than needed — and spots where predict-then-reconcile earns its keep.

That's the spectrum. You can't have the truth and the speed at once, so you decide — per operation — which to buy. Next module, the state grows past one node, and a new fact takes over: something is always failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment