Under the Hood

Scaling story & retrospective: the seams were the real design

The closing chapter, and the honest one. Fable runs on a single VM with a single Postgres, no replicas, no shards, no queue — and that's not a confession, it's the design working. This chapter lays out the scaling ladder in the exact order Fable would climb it, showing that the real engineering wasn't building distributed infrastructure but leaving the seams where it could be added: ULIDs and group-scoping for sharding, a wired-but-idle Redis adapter for realtime, idempotency everywhere for at-least-once. Then a frank retrospective — what the constraints got right, what broke in production, and the one lesson the whole series is really about.

Scaling story & retrospective: the seams were the real design

Here is Fable's production footprint, stated without apology: one virtual machine, one managed Postgres, one Node process, no read replicas, no shards, no message broker, no consensus system. In a system-design culture that equates sophistication with distribution, that can read like an admission. It isn't. It's the requirements and constraints working exactly as intended — the simplest thing that meets the requirement, with every piece of complexity refused until it earns its place. This final chapter is about what happens when pieces start earning their place, and then an honest look back at what the whole exercise taught.

The thesis: the real design work was never going to be building the distributed system. It was leaving the seams where the distributed system can grow without a rewrite — and, just as importantly, knowing the exact order to add things so you never build a piece before its problem is real.

The scaling ladder, in order

Each rung is a specific pressure, a specific response, and — the point — a seam that's already in place. You climb them in this order because each is cheaper and more likely-sufficient than the next:

Rung 0 — Vertical scaling. The first response to load is a bigger VM and a bigger database instance, and it goes much further than folklore admits. For an app Fable's size, this alone likely covers a very long runway. Seam: none needed — it's a config change.

Rung 1 — Read replicas. When reads (group screens, balance queries) saturate the primary, add a read replica and route reads to it. The pressure this creates: read-your-writes — a user's just-added expense vanishing from a lagging replica. Seam already understood: the fix is routing (a user's post-write reads go to the primary for a window), and it's a documented, anticipated bug, not a surprise.

Rung 2 — A second API instance. When one process can't serve the traffic (or you want failover), run two behind the load balancer. This is the interesting rung, because it simultaneously triggers three things — all of which Fable has already prepared for:

Rung 3 — A message queue. When async work (push fan-out, media processing) grows heavy enough to slow requests or needs to survive downstream outages → add a broker, and emit events via the outbox pattern. Seam already in place: idempotency is everywhere, so the "at-least-once delivery + idempotent consumer" half is already solved.

Rung 4 — Sharding. The last resort, when one primary can't hold the data or absorb the writes → shard by group_id. Seam pre-paid at schema-design time: everything is group-scoped so group_id is a natural shard key, and ULIDs mean no global-sequence coordination problem — the two properties that make future sharding almost mechanical, bought years early for free.

Rung 5 — Multi-region / consensus. Only if the product goes truly global and needs active-active writes → and this is the rung that drags in consensus and abandons the single-writer simplicity that makes a money ledger tractable. It's a serious re-architecture, correctly the last thing, and quite possibly never.

Retrospective: what the constraints got right

Reading back over the series, the constraints — one developer, tight budget, mobile client — consistently produced better engineering than more resources would have:

  • The invariants held. Exact money, sortable client-generated ids, ledger-as-projection — the small set of things that had to stay true survived every migration and feature, because they were named as invariants and defended, not left implicit.
  • Simplicity was forced, and it was right. A single-writer monolith meant money operations stayed local ACID transactions — no sagas, no 2PC, no distributed consistency bugs — which for a ledger is worth more than any scaling headroom.
  • The seams cost almost nothing and saved everything. The design decisions that made future scaling cheap were mostly free at the time (an id format, a scoping column, a wired adapter), and they're the difference between "add a rung" and "rewrite."

Retrospective: what broke

The honest part, because the whole series is built on production reality, not the happy path. The war stories, collected:

Every one of them shipped, was caught, and was fixed — and every one taught something that became a lesson in this site. That's the pattern worth internalizing: you will get things wrong in production; the discipline is invariants that contain the damage and a willingness to tell the story afterward.

The one lesson

If the entire Fable, Deconstructed series and the curriculum it's woven into reduce to a single idea, it's this: the hard engineering skill is not assembling sophisticated systems — it's knowing exactly which sophistication to refuse, and leaving the seam where you'd add it later. Every "you probably don't need this yet" — load balancers, sharding, consensus, microservices, a message broker — is the same point from a different angle. A design you can fully justify, that meets its requirements, that names its own trade-offs, and that can grow without a rewrite is worth more than any diagram full of boxes you added because they looked serious. Fable is one VM in Mumbai, and it is exactly as complex as it needs to be. That was the goal the whole time.

Interview takeaway

  • Present the scaling ladder in order, with the trigger for each rung. "Vertical first, then read replicas, then a second instance which forces sticky sessions and distributed rate limiting, then a queue, then shard by the tenant key, and multi-region essentially never" — this is the wrap-up step, and it shows you know both what to add and when.
  • Name the seams you'd leave. "I'd use a tenant-scoped sortable id from day one so sharding is mechanical later" is a senior sentence: cheap now, saves a rewrite.
  • Say the quiet part: "one box handles this, and I wouldn't distribute it until a specific pressure forces me." Refusing unnecessary complexity, out loud, is the strongest signal you can send.

That's the series. The theory is in the curriculum; Fable is what it looks like when the theory meets one developer, real users, and a single VM that turned out to be enough.

Go deeper

Check yourself

Answer out loud, as if an interviewer asked. If you hand-wave, reread that section.

  1. State Fable's production footprint, and explain why it is described as the design succeeding rather than the design being unfinished.
  2. Walk the scaling ladder in order (rungs 0–5). For each, give the pressure that triggers it and, where Fable has one, the seam already in place.
  3. The second-API-instance rung triggers three separate problems at once. Name all three and the prepared response to each.
  4. Explain the two opposite mistakes (premature scaling and un-growable simplicity) and the "leave the seam, skip the machinery" discipline that threads between them. Give two concrete seams Fable left.
  5. Which two properties of the data model pre-pay the cost of sharding, and why does paying them at schema-design time make future sharding mechanical?
  6. State the single lesson the series reduces to, and explain how "you probably don't need this yet" for load balancers, sharding, and consensus are all the same point.