Product architecture defaults to the server because that is where the data and the trust live. But the device in front of your user is not a terminal any more. It has multiple cores, gigabytes of memory, a persistent database, and the ability to run compiled code at close to native speed. A meaningful share of the work we push to servers is there out of habit.
Where local execution wins outright
- Search and filtering over a bounded dataset. If the user's working set is a few thousand records, ship it once and filter instantly rather than making a request per keystroke.
- Document and image manipulation. Compression, resizing, format conversion, and parsing all run well in WebAssembly and avoid uploading data the server does not need.
- Draft state. Every keystroke does not belong in a database. Persist locally, sync on a schedule, and the offline experience becomes free.
- Validation and preview. Immediate feedback with server confirmation afterwards is nearly always better than waiting for authority.
Local-first is a data model, not a cache
The distinction matters. A cache assumes the server is the truth and the client is a temporary copy. A local-first design treats the client's copy as a first-class replica that can accept writes offline and reconcile later. That requires deciding, per data type, how conflicts resolve: last writer wins, merge by field, or explicit user choice.
Most applications need this for only a small slice of their data, and getting that slice right is what produces the experience users describe as fast. The rest can stay server-authoritative.
The costs to weigh honestly
Shipping data to the client means shipping data to the client: consider what a user could read if they opened developer tools, and never send rows they should not see. Storage quotas are real and eviction happens, so treat local data as durable but not guaranteed. And bundle size is a genuine tension, since a WebAssembly module that saves a round trip but costs half a megabyte may lose on first load and win on the tenth.
The best round trip is the one you did not make. The second best is the one the user never waited for.
A pragmatic starting point
Find the interaction in your product with the highest frequency and the smallest payload, usually a filter, a search box, or a toggle. Move it entirely to the client with optimistic local state and background reconciliation. Measure the change in interaction latency and in server request volume. That single change usually pays for the pattern and tells you whether the rest of the application deserves the same treatment.
Join the conversation
Discussion0
Join the conversation. Sign in to leave a response or reply to comments.