Figma's database stack grew roughly 100x between 2020 and 2024. Rather than rewriting the application to be shard-aware in one step, the team vertically partitioned first, then introduced logical sharding, where queries still ran against a single database but were routed as if it were already sharded. Why is that intermediate step worth its cost, and what does it find that a cutover cannot?
Show the full answer Hide the answer
The situation they were in
Figma has published its database scaling work in detail. Between 2020 and 2024 the stack grew about a hundredfold. The first lever was vertical partitioning — moving groups of tables onto their own database, driven by a hard-coded table-to-partition configuration. It is cheap, it is reversible, and it bought substantial runway without any application changing how it writes queries.
Horizontal sharding was the harder project, roughly nine months, and it was split deliberately into logical sharding (queries are routed as though the data were sharded, while all of it still lives in one database, implemented with Postgres views that accept reads and writes) followed by physical sharding (the data actually moves).
Routing runs through DBProxy, a service between the application and the connection pooler carrying a lightweight query engine that parses and executes sharded queries.
Why the intermediate step pays for itself
It separates two failure classes that have completely different consequences. A routing defect returns the wrong rows: a bug, fixable forward, with the data intact. A data-movement defect loses or duplicates rows: a restore, with an RPO conversation attached.
Doing both at once means every incident during the migration has two candidate causes, and the team debugging it cannot tell which. Logical sharding puts the entire routing layer into production under real traffic while every row is still in one place, so routing can be wrong without being dangerous, and rollback is a configuration change rather than a reverse migration.
What shadow planning finds that a cutover cannot
Figma built a shadow planning framework: potential sharding schemes are defined for tables, the logical planning phase runs over live production traffic, and the queries and plans are logged for offline analysis.
What that enumerates is the queries that cannot be routed — cross-shard joins, scans with no shard key, lookups by a secondary key. In any codebase of real age these exist and you cannot find them by reading the code, because they live in ORM call sites generated at runtime, in admin tools, in background jobs and in one report written years ago by someone who has left. Running the planner over production traffic lists them empirically and ranks them by frequency, which turns "we think we are ready" into a finite worklist.
What it cost
- A query engine you now own. DBProxy must stay compatible with the semantics the application expects, and the set of SQL it must understand only grows.
- A new availability dependency on the hot path, holding connection state, with its own failure modes and its own capacity planning.
- Months of senior engineering time that did not ship product.
Where copying it would be a mistake, and when not to make this trade
The order is the lesson, not the destination. Vertical partitioning came first because it was cheap and reversible; sharding was justified only once a single primary could no longer carry the write load and the working set. A team whose writes fit comfortably on one primary that adopts a proxy-and-shard architecture on day one pays the operational cost for years before the constraint arrives, and pays it with a smaller team than Figma had.
The transferable parts are cheap and general: split a risky migration so that routing goes live before data moves, and use production traffic rather than code reading to enumerate what does not fit. Those apply at any size. The proxy does not.