Local-first is about building better apps. Apps are better for users because they’re instantly responsive, they enable collaboration and they’re resilient to the network dropping. But local-first also promises a better developer experience.
So what are the developer benefits of a local-first architecture and how compelling are they?
What's happening with local-first adoption
Local-first apps bring amazing speed and continuity to the user experience. So much so that, for apps that users spend a lot of time in, user experience benefits alone can be enough to lead developers to prefer a local-first architecture.
The project management app Linear is a good example of where gains in speed and network resilience add up to significant time savings and significantly less frustration for users — allowing a (now much-loved) product to be successful in a space that was already dominated by established incumbents. However, developers aren’t waiting for end-users to demand better UX before adopting local-first in their projects. The reason? A simpler developer experience.
Now, if local-first provides a simpler developer experience, why is local-first adoption only taking off now? Hasn’t it always been an option? The reason is that building your own sync engine used to be a huge barrier to local-first adoption. The complexity and engineering effort involved meant that it was only undertaken by the bravest or most passionate among us — like the folks at Linear. But now that sync engines are being productized more and more, developers can much more easily add them to their software.
So what benefits do you get from local-first, once you have a sync engine in place? Let’s look at 7 distinct developer benefits of local-first.
7 Developer Benefits of Local-First
1. Latency resilience
Local-first apps have super-fast UI because they use a local database with near-zero latency.
This fundamental shift in app architecture means that since data is stored exactly where it’s needed (in the user’s app or browser) the network is completely eliminated from the interaction path. And because of that, countering network latency is unnecessary: there’s no need for implementing caching on the front-end, using edge CDNs or deploying multi-region backends. Your app is resilient to network latency.
2. Forget the network
Since dealing with the network is abstracted away by sync engines, there’s no need to write networking code for app functionality or for most error handling code. For the most part, you can forget about the network.
Here's a pseudo code example given by Tuomas Artman that illustrates this difference:
Code example with a typical cloud-first architecture:
import React, { useState, useEffect } from 'react';
function DataFetchingExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect (() => {
fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError (error);
setLoading(false);
});
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<h1>Fetched Data</h1>
<pre>{JSON. stringify(data, null, 2)}</pre>
</div>
);
}
Code example after a local-first architecture has been adopted:
issue.comments.elements.map(comment => {
return <>{comment.bodyTextContent}</>
});
3. Simpler state management
State management becomes simple when your app state is just what’s in your local database. When underlying data changes, live query hooks made available by sync engines allow query results and the UI to be automatically updated. State management frameworks become unnecessary for most use cases.
4. Move logic to the frontend
Since all required data is available on the client, most logic can be moved to the client too. This can greatly simplify the backend.
It also allows new app features to be prototyped without having to consider the backend: queries run against the local database and updates are written there too.
Here's how Anselm Eikhoff explained the change in developer experience that local-first enables:
5. Less dependence on backend uptime
Since apps keep working with their local database even when the backend is down, there is much less of a need for site reliability engineering and for a team doing on-call rotation.
6. Reduced backend infrastructure costs
The simplified backend architecture that sync engines enable have a major effect on infrastructure costs. The picture given by Linear is: with the 1,000 concurrent users in their EU data center (for about 10,000 total) they “could run all of [that] with effectively just two CPU cores in Europe; which costs you maybe 80 bucks a month.”
7. Real-time streaming for free
The benefits of having a real-time sync engine as part of your stack extends to being able to easily add collaboration functionality like group chats or multi-user document editing. Built-in real-time streaming also makes it easy to push alerts or messages to users.
Video
Local-first clearly promises a great deal, but does it deliver? In the next few videos of the Local-First Key Concepts series, we’ll take a look at these benefits in more detail and show implementation examples.