Figma's databases team described sharding Postgres by first sharding logically - routing queries as if the data were already split - and putting a query proxy in front that parses SQL and sends each statement to the right shard, so application code keeps talking to "one database". What does preserving that abstraction buy, and what is the bill?
Show the full answer Hide the answer
What the abstraction buys
The alternative to a routing proxy is making every call site shard-aware. In an estate with thousands of query sites, that is a change to every team's code, coordinated, with no way to ship it incrementally and no way to roll it back once half the call sites have been converted.
Figma's published approach (engineering blog, 2024) separates the two hard problems. Logical sharding first means queries are rewritten to carry a shard key and routed as though the data were already split, while all of it still lives on one machine. Correctness of routing is then testable under production traffic before any data moves, and the step that moves data is a mechanical copy whose only risk is operational.
The proxy is what makes this possible: it parses each statement into a syntax tree, works out which shard the statement belongs to, and forwards it. The application keeps one connection string, so the migration is invisible above the proxy and reversible below it. That reversibility is the whole value. A shard-aware application cannot be un-sharded; a proxy can be pointed back at one database in a configuration change.
What it costs
Every query shape the proxy does not understand is an outage waiting for the first caller who writes it. A hand-written rewriter must cover the SQL your estate actually uses, including the constructs an ORM emits that nobody wrote on purpose. The work is not the common path; it is the tail, and the tail is discovered in production.
Cross-shard guarantees go away and the abstraction hides that they have gone. A foreign key cannot be enforced across shards. A unique index is unique per shard. A transaction spanning two shards can commit on one and fail on the other. The connection string still looks like one Postgres, so application code written against the old guarantees compiles, passes review, and is wrong.
Cross-shard reads become scatter-gather. A query without a shard key fans out to every shard and waits for the slowest. A thirty-shard fan-out has a p99 close to the p99 of the worst shard, so tail latency stops being an average and becomes a maximum — the single most common surprise after a sharding project lands.
The proxy is now on the critical path for every query. It is a new tier to deploy, size, observe and keep compatible with the database's wire protocol across version upgrades.
When the bill arrives
Not at cutover. It arrives at the first query shape the proxy mis-routes silently, at the first cross-shard write someone assumed was atomic, and at the first analytical query a product engineer writes without a shard key. All three are months after the migration is declared done, which is why the mitigation is a policy and not a code change: reject queries without a shard key by default, and require an explicit, reviewed annotation to permit a fan-out.
When not to build this
If one Postgres with read replicas still fits, this is the wrong project. The sequence that precedes sharding is boring and usually sufficient: fix the queries, add the indexes, move the large append-only tables out, then vertically partition by domain onto separate instances. Figma's own account starts from a single instance and reaches horizontal sharding only after vertical partitioning stopped being enough.
The decision rule: shard when write throughput or working-set size exceeds one machine, not when read load does. Replicas fix reads. And if the estate has a few dozen query sites rather than thousands, making the application shard-aware is cheaper than owning a SQL parser.