Lesson 06 · Sequencing the scaling moves

The Cheap Move First

Every previous lesson taught a technique. This one is about order — which is the thing that actually separates teams that scale calmly from teams that spend a year on a migration they did not yet need.

The win in this lesson: you will have a ladder of scaling moves ordered by cost, and a defensible answer to the question that ends most architecture arguments — "why not just shard?"

1. 100×, and what they did about it

Figma's database stack "has grown almost 100x since 2020". That is the kind of number that sounds like it must have required something heroic. What it actually required, for most of that span, was a sequence of unglamorous moves made in the right order.

By late 2022 they had "built out a distributed architecture with caching, read replicas, and a dozen vertically partitioned databases". Read that list against this workspace: read replicas are Lesson 01, caching is Lesson 03. They had done every cheap thing first.

2. Vertical before horizontal

The move that did most of the work is the one that gets written about least:

"We split groups of related tables—like 'Figma files' or 'Organizations'—into their own vertical partitions."

Figma, How Figma's databases team lived to tell the scale

This is worth being precise about, because the two words sound like variants of one idea and are not:

VERTICAL — different tables, different databases database A files, file_versions whole tables, all rows database B orgs, teams, users whole tables, all rows each table still lives in exactly one place. Queries and joins inside a partition are unchanged. HORIZONTAL — one table, rows split across databases shard 1 files, rows A–H shard 2 files, rows I–P shard 3 files, rows Q–Z no single database holds the table. Joins, transactions and unique constraints all become your problem.
Vertical partitioning moves whole tables and leaves SQL alone. Horizontal sharding splits a table and takes your database's guarantees with it. That difference is the entire argument for doing one before the other.

Figma's own verdict on the cheaper move:

"Vertical partitioning was a relatively easy and very impactful scaling lever that bought us significant runway quickly."

Figma

Runway is the right word and the right goal. The move did not solve scaling; it bought time, during which the company got bigger, the team got larger, and the eventual hard migration was done by more people who understood the system better. Deferring a hard problem is not procrastination when the deferral makes you more capable of solving it.

3. Knowing when the cheap moves are exhausted

"Despite our incremental scaling progress, we always knew that vertical partitioning could only get us so far."

Figma

The limit is structural, and you can see it in the diagram: vertical partitioning divides by table, so the moment a single table is too big for one machine, you have run out of ways to divide. Figma's largest were "several terabytes and billions of rows". No amount of further splitting-by-table helps, because the problem is inside one table.

That is the signal to move, and it is a good one precisely because it is objective. Not "we feel slow" — one table no longer fits.

4. What the expensive move actually cost

Nine months of work, and a new piece of infrastructure: DBProxy, a query routing service, built because the guarantees a single database gave them for free no longer existed once rows lived on different machines. The first horizontally sharded table shipped in September 2023, with "only ten seconds of partial availability on database primaries".

Ten seconds is an extraordinary result and it is the wrong thing to take away. Take this instead: nine months, and a service you now own forever. That is the true price of the bottom rung, and it is why the rungs above it are worth climbing carefully.

5. The ladder

MoveBuysCostsLesson
1. Index it, fix the queryOften an order of magnitudeAn afternoon
2. Bigger machineEverything, brieflyMoney, and a ceiling
3. Read replicasRead capacity, survivabilityStaleness, and read-your-writes bugs 01
4. CachingAbsorbs repeated readsInvalidation, herds, stale sets 03
5. Vertical partitioningRunway, quicklyCross-partition joins and transactions end this one
6. Horizontal shardingWrite capacity, and no ceiling Months of work; routing layer; SQL guarantees gone 02, 07
The rule, and the reason for it

Take the cheapest rung that clears your actual runway requirement, and re-evaluate when it stops working. Not because the lower rungs are better engineering — rung 6 is strictly more capable — but because every rung you climb permanently removes something you used to get for free. Joins. Transactions. Unique constraints. ORDER BY across the whole dataset. You are not buying capacity with money, you are buying it with guarantees, and you can never buy those back.

6. Check yourself

7. Back to your world

The next time someone proposes sharding, the useful question is not whether it would work. It is which rung are we on, and what exactly have we exhausted? If the answer is "we have not added an index yet", you have just saved nine months.

Ask me things. "what is DBProxy actually doing?" · "how do I know a bigger machine won't do?" · "what breaks first when joins cross partitions?" · "I think you should always design for shards from day one. Grill me."