The Fastest Network Request Is the One You Never Make

Keeping data and smarts on your own device, and why it changes the design

9 min read

Open a document in a cloud app on a plane and you get a spinner over text you already had. That is not a bug. It is the app being honest about how it was built. The server owns the truth, and you are just a window looking at it.

An old hydrographic chart of the world showing the network of submarine cables and telegraph lines strung between the continents.
Your data has a real address. Every request that crosses an ocean pays for the crossing in milliseconds, and no clever code makes the ocean narrower. Local-first is the decision not to cross it.Bibliothèque nationale de France, via Wikimedia CommonsPublic domain

In 2019, the researchers at Ink & Switch named the alternative. Local-first software keeps the main copy of your data on your device. It syncs in the background. Syncing means quietly copying your changes to other devices and copying theirs back. The network becomes a bonus, not a requirement. Think of it like this. Local-first is notes in your own notebook, always in your pocket. Cloud-first is notes you can only read when the library is open. They listed seven goals. Among them: it is fast, it works offline, it is your data, and it lasts forever.

In local-first applications, the availability of another computer should never prevent you from working.

Kleppmann, Wiggins, van Hardenberg & McGranaghanInk & Switch, Local-First Software, 2019

What actually changes in the interface

The loading spinner mostly disappears, and that one change spreads further than people expect. Once reads happen on your device, every action lands in under a tenth of a second. That is fast enough to feel instant, like flicking a light switch. Search updates as you type. Filters become play, not a careful request. Undo costs nothing. People start poking at their data instead of questioning it, and that change in behaviour is worth more than the milliseconds.

Local-first

background sync

Interaction

Local store

Render
~0-16 ms

CRDT merge

Peers / server

Cloud-first

Interaction

Network

Server

Network

Render
~300-800 ms

Syncing moves off the path of your click entirely. The network stops being a step and runs in the background.
  1. In-memory read100 ns
    ~100 ns
  2. On-disk local1 ms
    ~1 ms SSD
  3. Same-region server25 ms
    ~25 ms there and back
  4. Cross-continent150 ms
    ~150 ms there and back
Rough figures by scale, not measurements from one machine. Each step to the right is about ten times slower. Where your data lives decides which step you pay on every action.Typical figures; cf. latency numbers every programmer should know

The hard part is merging, and it is a design problem

If two people edit the same thing while offline, something has to decide what the result means. Picture two people editing one shopping list. This is where CRDTs come in. A CRDT is a way to merge two people's edits automatically, with no server picking a winner. It is the technology behind Automerge and Yjs. It promises that every device ends up with the same result. It cannot promise that result is what a person actually wanted.

Text merges nicely. A shared counter merges fine. But say two people each grab the last free room. The merge is mathematically valid and completely wrong in real life. So the interface has to carry the idea of a merge that needs a human. And someone has to design what that looks like, without making people doubt their own data.

  • Show the merge, do not hide it. When a background sync changes something in front of the user, mark it. Quietly changing what someone is looking at is the fastest way to lose their trust.
  • Keep the intent, not just the bytes. Store enough history to say Priya moved this to Tuesday while you were offline, instead of showing a bare change with no name on it.
  • Design for later, not instant. A sync badge should say synced, not saved. In a system spread across devices, saved is a small lie, and people feel it when it breaks.

background

no

yes

Local edit

Append to op log

Local state updates

Send ops to peers

Peer merges ops

Semantic clash?

Same state everywhere

Surface to the user

A CRDT sync loop. Your edits apply at once, then copy and merge in the background. Matching up is automatic. Only a real-world clash needs a person.

Now put the model on the device too

The same logic applies to AI. Running a hosted model means a trip to a server and back, which costs several hundred milliseconds before the first word appears. Fine for a big question. Too slow for anything that should feel like typing. WebGPU arrived in Chrome in 2023 and is now widely available. It lets a browser tab run a small model on your own graphics chip, with no server in the loop.

Hosted model, first word~700 ms
Hosted, warmed up~300 ms
Small model on device~40 ms
Local shortcut or index~5 ms
Time to the first useful response. Where the model runs decides how fast the feature can ever feel.Order-of-magnitude figures; see W3C WebGPU and WebLLM benchmarks

This creates a choice that did not exist three years ago. Fast, constant help belongs on the device: autocomplete, ranking, sorting, search over your own notes. There it is free and private. Slow, high-value thinking belongs in the cloud, where the user has already agreed to wait.

Be honest: local-first is often the wrong choice

The case above is real, but it is only half the ledger. A capable on-device model is big, and it has to live in the device's memory to be fast. Take Phi-3-mini, a small model built to run on a phone. It has 3.8 billion parameters. Squeezed down to four bits it still takes about 1.8 gigabytes, and Microsoft showed it running on an iPhone 14, fully offline, at over twelve words a second. That is a genuine achievement. It is also 1.8 gigabytes sitting in memory, a large slice of a phone that is also running everything else. Switch apps and the system can evict it, so you pay the load again. Anything much bigger than a few billion parameters does not fit at all.

Cold start is the next bill. Before the first word, the device has to download that model, hundreds of megabytes to a couple of gigabytes, and compile it for the local chip. A server never charged the user for this. The first session on a new device does.

Then there is power. A cached read off local disk costs almost nothing. Sustained generation runs the GPU or neural chip hot, drains the battery, and warms the device in your hand. I will not pretend to a single universal number, because it depends on the chip and the model. But a laptop on battery throttles, and a phone gets warm, and users notice both.

Updating is the tradeoff people forget. A hosted model you improve once, for everyone, overnight. An on-device model is thousands of copies scattered across the world. Shipping a better version means every device re-downloads gigabytes, on its own schedule, and you keep supporting the old versions until they catch up. A flaw in the model stops being a deploy and becomes a fleet update.

So the round trip to a server is genuinely right more often than local-first fans admit. Send it away when the model has to be frontier quality, when it needs fresh or shared data the device cannot hold, when the task is rare enough that a short wait is fine, or when the device is too weak or too full to carry the model at all. Crossing the ocean is the correct call whenever what waits on the far side cannot fit on this side.

Keep it on the device
  • Fast, repeated actions on personal data
  • Works offline and stays private
  • A small model that fits in memory
  • The same task, many times a minute
Send it to a server
  • Frontier quality the device cannot match
  • Fresh or shared data the device lacks
  • A rare task where a short wait is fine
  • A weak or full device that cannot hold the model
Local-first is a wrong default for anything needing a frontier model, shared truth, or a tiny install. It earns its keep for fast, private, repeated work over your own data.

One more tradeoff cuts against the privacy story. Putting the model on the device also hands the model to whoever owns the device. The weights ship with the app, and a determined person can pull them out. If the model is the product, on-device means giving away the thing you sell. A hosted model stays behind an interface you control. Local-first protects the user's data and exposes the maker's model. Which one matters more depends on whose secret you are keeping.

It is also worth puncturing the idea that local-first means no server. Most real local-first apps still run one, to relay changes between devices, to hold a backup, to serve people who share a document but are never online at the same time. Ink and Switch call it a sync server, and the honesty is in the name. It syncs, it does not own. But it is still infrastructure you build, pay for, and keep alive. Local-first moves the server off the path of your click. It does not always delete it. Anyone selling local-first as free of servers is selling the poster, not the product.

And building that sync is not a weekend. Merging edits correctly, handling a device that was offline for a month, migrating the data format after you have shipped, these are the hard, unglamorous parts, and they land on you instead of on a database vendor. The instant, offline, private product the user feels is bought with real engineering the user never sees. That is a fair trade for the right app. It is a waste for one that a plain server would have served just fine.

Why I think this becomes the default

The same five things a product team argues about, settled by where the truth lives before a single screen is drawn.
What you compareCloud-firstLocal-first
SpeedA trip to the server every timeReads on device, instant
OfflineSpinner or deadFully usable
OwnershipServer owns the truthYour device owns the truth
Merging editsLast save on the server winsCRDT merge, always lines up
Cost at scalePay per call and per downloadAlmost nothing per call
The same five things a product team argues about, settled by where the truth lives before a single screen is drawn.

Privacy rules keep getting stricter. AI costs keep mattering as you grow. And people keep learning that software they do not control can vanish. Local-first answers all three at once. The data never leaves. The AI costs nothing per call. And the app keeps working even when the company behind it does not.

That last point is the one that moves me. Almost everything we build today dies the moment a server is switched off. Local-first is the first approach in twenty years that takes one idea seriously: a person's work should outlive the product that made it.

The merge problem does not shrink as you grow. It grows. Two people editing rarely clash. A team of fifty editing one shared space, half of them offline on a train, produces clashes daily, and every clash is a small design problem you now own. The cost of a merge that needs a human is paid by your users, in confusion, unless you have designed the moment it happens. Local-first hands you a better default and a harder edge case in the same box.

What to do on Monday

Do not port your whole app to a local database this week. Take the single interaction people repeat most, a search, a filter, a re-sort, and move just its data onto the device so that one action lands instantly. Leave everything else on the server for now. Then measure whether people use that feature more once it stops asking the network for permission. If they do, you have earned the case to go further. If they do not, you just saved yourself a migration you did not need. Let where the truth lives be a decision you earn per feature, not a religion you adopt all at once.

Was this useful? Your choice stays private to this device.