Linear is 21mb of minified JavaScript which is an awful lot. I made a dictionary progressive web app recently that just needed read only databases, so I rolled my own simple system complete with zstd compression all in a 38kb wasm module.
1. user clicks a button and closes the tab thinking transaction is done and it's important that transaction is done
2. conflict resolution is difficult or impossible in future client wake up
A background worker picks up the mutation and sends it to the remote backend. It takes time, retries, etc.
Similarly, any errors reported by the background worker go to local store, and the next time the UI tab is loaded / activated, they are shown. A service worker can show a notification outright to let the user easily load the main UI. Normally this would be a rare occasion.
More generally: You can't circument the trade-offs of a distributed database, which such products are, conceptually.
Main downside is it significantly complicates the front end code compared to just waiting for FE to sync with BE before updating
Though its depressing how few actually use it to its full extent. My team is one of the few where I work; heavy declarative mutation directives with optimisticResponse (and optimisticUpdaters because some of our APIs are not very Relay-compatible, annoyingly)
Relay does optimistic updates well. However, frustratingly, Relay does not do any persistent caching to disk, like Linear does. This means, first page load will always have to fetch data from the server.
But I’ve only done that in toy examples not prod, I’m sure there’s something I’m missing haha
Strange that we can be so be polar opposites on this. You hate it, I would never write an app in any other way, ever again.
As a developer, I hated the article and many of the comments I read thus far because:
- Having clients and a server properly sync and not lose data in the event of a network failure amounts to having a consistent distributed system which is not easy to do, and the commenters don't seem to have understood that
- I hate having written a long document and then losing it because the sync code is buggy, so the previous point becomes even more important.
So reading many of the things here has been mildly infuriating.
That being said, none of these people are likely affiliated with Linear, and given the overall quality of the product I'm pretty sure it works properly.
So no violation of CAP theorem it just prioritises liveness over consistency
Yeah that's the issue isn't it? I see in the UI it's sent. But actually it's sent only the next morning.
To be fair. It's fine for an issue tracker. Anything actually important i'd spend a few seconds going over what I just sent. In which case I'd see it's not synced. And what's not that important it's really fine if in some random wifi edge case it's phantom sent. So makes sense.
‹giant argument breaks out before people realize a bunch of messages went missing and were posted out of order› “Oh, it's just ‹app› being weird again. I really hate that.”
The reply with be delayed by days or weeks, but the UI indicated that it had been properly saved.
There's not. Browsers can delete "persistent" storage at any time.
https://developer.mozilla.org/en-US/docs/Web/API/Storage_API...
> If, for any reason, developers need persistent storage [...] they can do so by using the navigator.storage.persist() method of the Storage API.
This makes a request for guaranteed permanent storage ... which can be approved (or denied) by the user or by browser defaults.
You can never truly trust anything about a client because by definition you don’t control it
The whole concept of "assume it is committed while we sync in the background" is, in the most cases, a terrible architectural decision, unless it is coupled with explicit feedback (eg. A small visual indicator indicating if the background queue is empty or syncing). Also, it breaks temporality: last-update-wins no longer holds, because update time and sync time are decoupled. And you also create a new problem, which is local cache coherence.
It may be a good fit for some systems (though I cannot think of a single one), but in general is just a horrible solution.
E.g. if you buy a book, but it turns out the book was already sold, then you will first get a message "Your book is on its way!" and then "Oops, sorry, the book was already sold to someone else".
Eventual consistency is just a property of the database.
https://github.com/wzhudev/reverse-linear-sync-engine/blob/m...
Reverse engineering of Linear's sync engine - https://news.ycombinator.com/item?id=44123131 - May 2025 (33 comments)
Early days’ Trello was the best project tracking experience by far.
What about this requires more than a few ms (at most) on the backend, and how does a local copy make that better? It seems like a local copy would create even more inconsistency (and if it doesn't, the backend should be fast too!)
"A few milliseconds is all it takes to update an issue in Linear. A traditional CRUD app doing the same thing takes about 300ms."
"Any data sent between the client and server costs hundreds of milliseconds."
There’s no solving the problem of a large RTT between an HTTP client and server when it’s due to the speed of light.
But what you can do is locate the backend near users and make sure it’s fast.
For example, it’s very possible to run a web app backend within ~10ms RTT of most users and have the backend render responses within ~10ms too.
In other words, you can absolutely create a traditional CRUD app where doing the same thing takes more like 30ms, not 300ms.
What are you talking about? The only AWS region < 10ms away from us-east-1 is, err, us-east-2:
us-west-1 is 60ms away. eu-centra-1 is 100ms away. asia is 200ms away.
and this is datacenter-to-datacenter traffic. Actual latency over the public internet to residential providers is far worse.
Your database needs to be in exactly one region. So no matter where you put it, the majority of uses on earth are going to be > 100ms away from it.
It doesn't matter where the endpoints are, because the endpoints need to talk to the database to read and write data. Thinking you can replicate some data closer to the users? Congrats you are now the proud owner of a "local-first syncing" database. This replicated database you use (either of your own design or off the shelf) will have all the same problems of client-side syncing. Except you'll still have significant network latency.
There is no getting around physics. You either have quarter-second commits for most users or eventual consistency (aka syncing). Those are the options.
Only if your users are all located quite close to each other, or (sadly very common) you only care about making it fast for US users and screw everyone else.
(Of course you can have "intermediary backends" around the world on a CDN's edge network or similar, but at that point you're paying the same complexity cost as this style of putting the "intermediary backend" on the client)
But the happy path stays lightning-fast.
My point above is that the simple solution ("traditional CRUD app") is actually viable even when the goal is very low latency.
1. type
2. stop to ponder for a split second
3. type some more
4. Linear reverts data to step 1
It's bad enough that I'll use Linear to create issues with a single sentence description. This is what Linear is good and fast at. Then I'll switch to GitHub to fill in the details.
it just told me to f off. lol.
i bet this company got funded, imploded headcount, nobody see a profitable exit, and now they are all fighting each other for quarterly promotions based on whatever metric they can both improve and entice the cto. which i will go on a wild guess and say its page load performance.
It's really the only way for companies to survive and go up market, unfortunately.
"Fast" is not a word I'd use for it.
I don't care about going from 300ms to "a few" milliseconds to update an issue when it takes 30 seconds to load the thing in the first place.
For example, the search field only shows if you press "ctrl-f".
It’s more like Applification. Apple removes every hint the user needs to know how to use its UIs in the name of “simplicity,” which makes them undiscoverable and complex.
This wave of business apps (Notion is another one) takes things one step further and hides even more things, even the simple stuff. They make it hard to do even a lot of the things I'd consider bare-minimum. The Atlassian apps were't much better, and were in many ways even more annoying, but at least they didn't hide the basic biz-app functionality as much.
As for optimistic routes and "fast" - maybe we ought to talk gmail first?
300ms seems like a lot. Even if this includes the whole http request+response it still seems unbelievably long.
RTT from Hyderabad to the East Coast USA is ~300ms.
Then you have execution and database retrieval.
but the benchmark is similar software like JIRA, which takes agonizingly long to do anything reasonable.
Also I couldn't see any explanation of what happens when a network request fails. That's a huge downside of local rendering.
I think for most sites the best option is still server-side rendering but use a fast backend (e.g. not Python) and lightweight frontend.
One thing I would like to point out though is that building a performant sync engine that behaves the way you would like in most cases is a non-trivial thing.
If two users are offline and add, edit, remove issues and come online again, you need to reason about what happens. Sometimes you can get away with Last Writer Wins but what happens if an issue is deleted and then edited. What happens if an item in a list gets re-ordered differently on different clients, what's the final ordering? In which cases can you merge what state and in which do you need to discard something. Do you show a conflict resolution UI? How do you deal with rollbacks. How do you deal with schema drift and updates on items that would be affected by schema drift? Business logic might change between being reconnects. FK constraints can shift. Can you set up your data and the sync engine so that it only syncs the minimum amount of changes and batches them correctly during longer offline sessions so you don't fire 5000 change requests after reconnecting?
I recently had to implement local first + remote sync on some fairly complex dynamic forms and where luckily there is usually only one writer and I can get away with last writer wins and reject if things are too old or if there is schema drift and can just display an error message and roll back. But what I am trying to say is: Whatever can go wrong in an online-first world, can also go wrong in an offline first world but you might get informed of that all at once at a later time - or not at all and your data is not what you think it should be. Some sync engines like zero from rocicorp has opted out of supporting offline writes entirely because of all these problems.
And just to be clear: I love offline first approaches. I yearn for fast performance. And in a lot of cases slapping a sync engine on your app can really be helpful for that if your use case allows it. But it's absolutely crucial to be aware of the pitfalls that come with it.
based on my experience, this is a great description of the sync implementation in many already widely deployed products (say, off the top of my head, OneNote, OneDrive)
In the local first world you might have navigated away already and created 3 more issues of which 2 more failed because of schema drift or other conflicts. And you might have edited one that was deleted. And now you need to figure out what exactly to tell the user - or what not to tell them.
Warn the user that if they leave the website their changes won't be saved remotely.
Exhaust all other optimizations before lying to your users about what just happened.
I would not implement optimistic resolution in key information, prices, for example. They live in the server and backend should keep the total control in the client side, even feedback response time.
Bottom line is, if your app's content is behind a login screen, just use client side rendering. It is way lower complexity and a way better user experience.
What's crazier is they support this navigation key combo in their command palette but no where else.
The vim line nav is a nice thing in apps, but it's extremely frustrating I can't change it.
Linear IS essentially a crud app so saying crud app does it in 300ms doesn't mean anything in this context imo.
If you have a database stack that is actually fast. And you can use something that is actually fast on the frontend like solidjs. Then you might have something that is actually fast.
But putting more complexity and caches etc. on top of it will leave you chasing issues that cause performance cliffs forever.
Modern hardware is insanely fast, this kind of complexity shouldn't ever be needed even if you consider each individual person is related to thousands of issues. I think most people have a home internet connection that is 100Mbps/s at least and they are on CPU with more than 4 cores and more ram than 8GB. And the frontend is running in a browser like chromium or firefox which is also insanely optimized.
Writing backend in JS, using postgres for database, then also using the clunkiest frameworks for the frontend and then writing something is highperformance is a really high level of delusion
Yes, I spent a few months. But it worth it. Every new field, model I need to add, it is so straightforward. I do love frameworks and foundations. They make live easier later by a lot.
https://m.youtube.com/watch?v=WxK11RsLqp4&t=2175s&pp=2AH_EJA...
These are original video. Very clear and a way better than the blog post.
https://performance.dev/skills
https://performance.dev/how-is-linear-so-fast-a-technical-br...
I am genuinely impressed with their engineering and design — I aspire to attain these levels, though I lack not only the skills, but also several zeroes in my bank account, I think. Still, it's worth looking at what they do and try to get there!
Big props and kudos to the Linear team. It's an impressive app.
I was thoroughly unimpressed. "This is it?" - "It's a tracker"
I feel comfortable saying this because in the weeks after, actually using it is the aha experience. Linear nailed the UX of working where people already work. Which again is really funny because the best part of Linear is how well it works outside of Linear.
(disclaimer: I actually now use their UI a lot. It's a helpful dashboard. But it suffers from every other hard-problem of information dense task-based dashboards.)
Seriously, I hate it, but I've worked in most other competing products, from Trello to Azure Devops to Linear, Jira is a much more powerful engine that can be easily adapted to large organizations where each team has very different processes.
What works for a software team, does not work for sales, which does not work for HR, which does not work for QA or business development. Jira is flexible enough that can accomodate any kind of operation. Linear is like a very small and catchy subset of it.
Whenever I use it, I don't feel like I'm doing anything new when compared to all the other issue trackers and Kanban boards I've used before.
A leader of 6 will spend a lot of time in such an app, so the UX is valuable and a differentiator for them.
IMO a good approach is to update the UI immediately but still show some indication that the operation hasn't completed. So in a chat app, for example, add the message to the list of messages, but with contrast reduced slightly to indicate that other people can't see it yet.
Meanwhile after a brief period of Jira being performant, it has felt into ruin again.
In any case, I've tried both, and Jira is on another whole level when it comes to map processes of different teams.
Linear is a good looking toy mostly catered to the average software engineering team, it just doesn't support the flow complexity needed by different business units.
https://github.com/tinyplex/tinybase seems kinda good maybe?
I loved meteorjs and its still my fav framework to this day.
Seeing optimistic ui and browser based databases being used this way makes me happy.
I would like to understand how they do the chunking and only updating part of the graph instead all of it.
And it was doing exactly what is described here, using a reimplementation of mongodb, in the frontend: minimongo.
This is basically a thick client, and comes with according trade-offs. It's interesting and there are some best practices, but I can't help but feeling that either the author is a huge fan or the post is an ad (or "sponsored").
He creates a task called "Create faster app launch", if we believe the article, it's processing that locally rather than going via the server, and then it's allocated an ID "BRO-5". That the ID is so low suggests it's just adding one to the previous issue ID, and so under heavy load, there are almost certainly going to be conflicts with other users creating tasks and getting identical IDs. Even if the system resolves this by changing one of those IDs, the system shouldn't be presenting the ID to the user until it is guaranteed unique. What if they've already pasted it into a document when the system notices the collision and renumbers it?
[1] https://media.performance.dev/posts/p_gAMR6Z7y49Fp/NZrXs70M_...
EDIT: WTH, there are some seriously bad karma people in this thread - just because I dared to have an opinion that the approach taken by this software might not be the best, my post was downvoted in less than a minute after posting! I'm sure whoever did that carefully considered my argument. If I'm wrong, explain why, don't just downvote my comment. If I'm not wrong, shame on you.
He chose the path to a better product rather than the path to a quick buck. That is definitely odd for a silicon valley startup
Feels like AI slop.
Doesn’t address the concurrent update problem except for “optimistic”. At least provide some data why that’s ok.