System preferences

Back

SECTOR 02.1Transmission open

The server is just a cache

Why is Linear fast? Fast like local software, on a network product, with multiplayer.

The obvious answer is good engineering. It is wrong. Linear is fast because of one inversion, and the details are public. Tuomas Artman's talks lay out the engine. An obsessive reverse-engineering repo maps it, and Artman calls it "probably the best documentation that exists". The client owns a replica, and the server only orders and reconciles. The industry named the family: sync engines. The server is becoming a cache.

Linear versions the whole database

Every transaction the server accepts increments one global counter, lastSyncId. That number is the version of the entire database. Call it the watermark.

Each client keeps an IndexedDB replica. It records the watermark it has seen and the syncGroups it belongs to. Permissions live there, as membership in a replication stream rather than as endpoint auth. Your replica never receives rows outside your groups.

A mutation enters a persisted TransactionQueue, and the MobX object graph updates at once. The client never writes model tables from its own mutations. Memory goes optimistic. IndexedDB waits for the server's confirming delta packet, ordered, with side effects applied. The server may compute a result the client did not predict. A workflow trigger fires. An automation renames the thing. The confirmed delta wins.

Two features fall out of the log. Every transaction knows how to build its inverse, so undo is free. The queue grows while the socket is down, so offline is free. Bootstrap is tiered, so a giant workspace does not block first paint. Some models hydrate at once, some lazily, some partially.

Zero and Electric put the watermark in the protocol

Linear hand-tuned one product. The next generation generalizes it in two directions.

Zero makes queries the unit of sync. The client registers ZQL queries. zero-cache tails Postgres logical replication and maintains each client's result set with incremental view maintenance. It diffs a per-client version map, so only deltas travel.

CODE // TRANSMISSION03 LINES
const [issues] = useQuery(
  z.query.issue.where('assignee', me.id).orderBy('updatedAt', 'desc')
)

Electric makes replication HTTP-shaped. A shape is a table plus a where-clause plus columns, streamed as a log you read with plain GETs.

CODE // TRANSMISSION02 LINES
GET /v1/shape?table=issues&offset=-1        → snapshot + electric-handle + electric-offset
GET /v1/shape?table=issues&offset=0_128&handle=…&live=true   → long-poll for deltas

The watermark rides in the query string, and control messages ride the same stream. up-to-date says you are current. A 409 must-refetch says the shape definition changed, so discard the replica and resync.

CDNs collapse the fan-out because the read path is GETs with offsets. The 1.0 GA post claims a million concurrent clients off one commodity Postgres. The claim is believable because the protocol is boring. CDNs do what CDNs do. Reads only, though. Writes go through your API like it is 2015, and Electric resolves nothing.

The mutation runs twice on purpose

Optimism is easy. Reconciliation is the product, and all three systems landed on the same shape. Zero's custom mutators make it explicit.

CODE // TRANSMISSION04 LINES
// runs twice, by design
async function completeIssue(tx, { id }) {
  await tx.issue.update({ id, status: 'done', completedAt: Date.now() })
}

The client runs the mutator against the local store, so the UI moves at once. The server re-executes the same mutator with authority. It consults permissions, runs side effects, and sees mutations from other clients that landed first.

Then the client rebases. It discards its speculative result, applies the server's, and replays any unacknowledged local mutations on top. Git semantics for app state. If the speculation matched, nobody notices. If it missed, the UI corrects in one frame instead of drifting forever.

Figma rejected CRDTs in 2019 and the field copied the rejection

The reflexive assumption is local-first, so CRDTs. The systems winning in production mostly said no.

Evan Wallace wrote the canonical rejection in 2019. A central server removes the need for distributed timestamps, because "the server can define the order of events". Figma runs per-property last-writer-wins plus two tricks everyone since has stolen.

Fractional indexing orders children. A position is a fraction between its neighbors, so insertion is one property write. The known wart arrives when two users insert at the same gap. Flicker prevention masks conflicting server updates until the ack lands. That is the rebase again, under another name.

Linear orders transactions centrally and lands OT-adjacent. Zero re-executes on the server. A central sequencer plus a client rebase wins.

CRDTs keep the ground where their guarantees are the product: collaborative text, true peer-to-peer, and offline-heavy tools with no authority to appeal to. They guarantee convergence. They do not guarantee intent. Two people editing the same issue title converge under last-writer-wins. Two people editing the same sentence need character-level merge semantics. Most SaaS is issue titles, not sentences.

Offline is a product decision with a price. Zero's docs support offline reads while the team plans to restrict offline writes during beta. An unbounded offline queue rebasing against a week of teammate changes produces conflicts no algorithm resolves meaningfully. Linear queues them happily, and its domain is the friendliest case: small, discrete, per-field writes.

Name the family honestly. This is the server-authoritative fork of local-first, and it fails the last three of Ink & Switch's seven ideals on purpose. The server holds the authority, and a company holds the server.

Complexity relocates, never dissolves

Four bills arrive with the replica.

  1. Partial replication. Which rows does this client get, and what happens when the answer changes. Zero diffs its version maps, Electric 409s the shape, Linear recomputes sync-group membership. Every answer needs a path where the client forgets what it knows. Plain REST never needed one.
  2. Permissions in the stream. Get a filter wrong and you sync a salary column into someone's IndexedDB. No DELETE endpoint un-ships it.
  3. Version skew. Schema migrations must tolerate clients running last month's code against yesterday's local data.
  4. Per-client state. You pay for CVRs and sync groups, replication CPU, and client storage quotas, in exchange for reads that cost nothing at request time.

Where the watermark lives decides who pays that fourth bill. Electric keeps it in the URL, so the CDN carries the fan-out. Zero and Linear keep it on the server. Three bets: plain HTTP, incremental view maintenance, and one hand-tuned product. Nobody bets on free.

Where it fits

The fit is workspace software. A user stares at it for hours and mutates constantly. The working set fits a laptop. Collaboration happens per field.

The non-fit is content sites, payment flows, and anything where the replica is dead weight or a liability. Text-collaboration cores stay with CRDTs.

Rule of thumb. If users would notice going offline mid-session, you are a sync engine candidate. If they would just leave, keep your fetches.

Request/response was never a law of nature. It was a reasonable answer from 1994. Thirty-one years later the client has gigabytes of RAM and a database engine in the browser. The workload is 95% reads of data it already saw. The server keeps the watermark. Everything else lives on the laptop.