How's Linear so fast? A technical breakdown
Posted by howToTestFE 2 days ago
Comments
Comment by ricardobeat 2 days ago
Early days’ Trello was the best project tracking experience by far.
Comment by Aurornis 1 day ago
Whatever they did to market the product worked amazingly well, because I see it being suggested all the time even by people who haven’t used it! Their marketing has established it as the fast alternative, so every complaint about slow project management elicits a suggestion from someone to try Linear. Then you ask them about their own experience with Linear and either they haven’t used it or didn’t use it for more than a few hours on a side project.
Comment by mgdev 1 day ago
Not-JIRA + dark mode + usable APIs will take you far.
Comment by s08148692 1 day ago
Comment by port11 1 day ago
Linear was really, incredibly fast. We adopted it in 2021 and it was amazing.
Now it’s bloated. That’s the standard path for most software (Zawinski’s Law), and I’m not very excited when they ship another feature. Most of the new stuff isn’t for small players, they’re more of an alternative for medium and large businesses.
Comment by blanched 1 day ago
Comment by port11 1 day ago
Comment by theshrike79 1 day ago
The Atlassian shared hosting one is a nightmare.
Comment by difu_disciple 1 day ago
Comment by stronglikedan 1 day ago
Comment by stevoski 2 days ago
They keep adding new features at a steady pace, each slowly making the UI more confusing.
Comment by kardianos 1 day ago
The core issue isn't hard so I built my own, native OIDC auth, status, comments, plus I made communication a first class citizen, tracked it its own status workflow. Another nice thing I like is that status has attached a workflow that associates actions with a status transitions and task screens are defined in data. All APIs go through a single endpoint that can be intrinsically combined into batches. Still fast. It isn't that great, but I like it so much better then any of these commercial offerings.
Comment by nolander 2 days ago
Comment by tipiirai 2 days ago
Comment by mb2100 1 day ago
Comment by stevekemp 2 days ago
I'd get 10+ tabs all stuck in a "loading" state, and even force-reloading wouldn't make their contents match the address-bar.
Comment by mb2100 1 day ago
Comment by mikkelam 1 day ago
Comment by 20k 2 days ago
Comment by benjaminwootton 1 day ago
Comment by 20k 1 day ago
Comment by jacobgold 2 days ago
"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.
Comment by evantbyrne 2 days ago
Comment by SoftTalker 2 days ago
Comment by lmm 2 days ago
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)
Comment by aforwardslash 2 days ago
Comment by camgunz 1 day ago
Very true, mostly because it's pricey to do so--or more specifically, putting a server in DCA is $.005/user, putting a server in JNB is like $.50/user sooooo....
Sometimes this is a "so what", other times it has huge implications for app architecture.
Comment by aboodman 2 days ago
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.
Comment by jitl 2 days ago
taken to extreme, cloudflare durable objects & workers let you place data very close to a tenant automatically; but you lose total write throughput on top of sqlite.
Comment by lucaspiller 2 days ago
Optimistic updates on the frontend are probably simpler too.
Comment by jitl 1 day ago
Comment by aboodman 2 days ago
Comment by nijave 1 day ago
Each individual user is fast due to close geo and everyone else has a small (potentially trivial) lag to see writes.
Not sure if such an architecture is worth the complexity, but it's definitely possible.
Actually such architectures are quite old. Back when I worked at Kmart, they had a store server in the office of every store. The store server would asynchronously sync back to corporate (afaik an overnight cron but I think it could be triggered on command). That was the geographically close "edge" server and the store was the tenant. Most ops were quick. For cross tenant queries, clients maintained a list of store numbers and locations. They did some bit twiddling with the store number to calculate a deterministic IP which went to the store server for that store (tenant discovery). With the server IP they could run remote queries directly at the cost of much higher latency since you had to go back through the corporate S2S VPN to headquarters then to the target store.
As for cross geo, you can have writes always be instantly acknowledged at the closest geo location and immediately available to nearby clients while they get asynchronously replicated in the background. Really you'd only see marginal higher write latency when two people are working at the same time in different geographies. That's partially mitigated with time zones
Comment by aboodman 1 day ago
Comment by bastawhiz 2 days ago
Comment by aboodman 2 days ago
Again, AWS latency us-west-1 to us-east-1 is 70ms. That's absolute best case for one round-trip that does absolutely no work. And it's ignoring the case of anyone outside of continental US.
Add in actual server-side work, db interactions, and contention - and you're quickly looking at hundreds of ms.
Comment by jacobgold 1 day ago
You're assuming a single global database, which ignores the many alternatives.
Comment by aboodman 1 day ago
These systems are designed for the very common case of a global user base. If you have geographically centralized users, you can maybe do something simpler. That is rare in my experience - basically all of our customers have users worldwide. They typically don’t even know where their users are so making ux tradeoffs based on that feels really risky.
But maybe my experience is different than yours. One of the amazing things about the software ecosystem is how big it is. Everyone thinks their view is the common case.
Comment by andersmurphy 2 days ago
Honestly client side animations go a long way to masking latency too.
Comment by nine_k 2 days ago
But the happy path stays lightning-fast.
Comment by jacobgold 2 days ago
My point above is that the simple solution ("traditional CRUD app") is actually viable even when the goal is very low latency.
Comment by throwaway7783 2 days ago
Comment by nine_k 2 days ago
Comment by throwaway7783 2 days ago
Comment by pastel8739 2 days ago
Comment by bastawhiz 2 days ago
Comment by swiftcoder 1 day ago
Comment by aboodman 2 days ago
Live demo: https://gigabugs.rocicorp.dev/.
We also list some alternatives here: https://zero.rocicorp.dev/docs/when-to-use#alternatives.
If you're interested in how these things work internally, check out the Replicache design doc: https://doc.replicache.dev/concepts/how-it-works. Replicache was the predecessor to Zero and the core protocol still works the same way.
Comment by thingortwo 2 days ago
```
const markAllAsRead = defineMutator( z.object({ userId: z.string() }), async ({tx, args: {userId}}) => { // shared stuff ...
if (tx.location === 'server') {
// `tx` is now narrowed to `ServerTransaction`.
// Do special server-only stuff with raw SQL.
await tx.dbTransaction.query(
`
UPDATE notification
SET read = true
WHERE user_id = $1
`,
[userId]
)
}
}
)```
Comment by aboodman 2 days ago
As for ZQL:
a) basically all of our customers already use Drizzle/Prisma. So they are very used to custom DSLs, and like them. I know, I was surprised to!
b) You typically use the same code client-side and server-side. There's no branching. The example you pasted is showing an escape hatch for when you want to use custom SQL. The option is there, but it's not the common experience.
This is what a typical mutator looks like:
```ts
// src/mutators.ts
import {defineMutators, defineMutator} from '@rocicorp/zero'
import {z} from 'zod'
export const mutators = defineMutators({
updateIssue: defineMutator(
z.object({
id: z.string(),
title: z.string()
}),
async ({tx, args: {id, title}}) => {
if (title.length > 100) {
throw new Error(`Title is too long`)
}
await tx.mutate.issue.update({
id,
title
})
}
)
})
```We are trying to make apps like Notion, Linear, Superhuman easier to create. These apps all uses custom-built sync engines that took their teams many person-years of effort to construct.
Whether this complexity is worth it depends on how badly you want instantaneous response. If you do, you will end up using sync one way or other, and you will end up with something roughly like Zero mutators.
Comment by thingortwo 2 days ago
I would use these DSL if they provide 10x improvement but it seems to me like a downgrade in every way I will need to rely on you to keep this thing running 10 years down the road and hope you are still in business. Whereas raw SQL will probably work as is since past performance is usually indicator of future and sqlite/postgres are 25 years old and if I recall correctly you already had this similar project that is now no longer maintained: https://replicache.dev/ so this by default makes me trust this project less since it will touch critical parts of my app.
Also, imo the custom sync engine path is usually better because most of this turns into logical replication unless you are syncing simple notes and then teams already know what they need and a last-write-wins + row_id,table => changes_log tables isn't that hard the issue is usually that the client and server will need to duplicate functionality and I will be a lot more comfortable duplicating those using raw sql queries since you write those ones and use on both sides. Any half-sync or other optimizations usually just end up causing a lot of headaches in a relational db with foreign keys on.
So, I would use something like this only if it is a sidecar process like litestream seamlessly doing it's thing vs becoming a main concern in my app core. But that again is the issue logical replication vs physical replication and how can a sidecar know the intent.
Comment by aboodman 1 day ago
But I get it. Unfortunately something like this cannot be a sidecar, or at least I do not know how to make it one. It's central to your app in the way that React is.
Fortunately the category is expanding and there are several sync engines that plug in exactly how Zero does - by replicating your database. You can switch between them easily. So as long as you think you do need a sync engine you aren't that married to Zero specifically.
Here's a demo of that!
Comment by thingortwo 1 day ago
My own app uses something like this and I have yet to encounter any issue whereas I did initially look into using electricsql , zero sync , instantdb , triplit but the amount of lock-in or DSL was not my cup of tea and most of these are now doing "AI Agents" marketing instead of their core original problem which further erodes any trust.
Comment by kobieps 1 day ago
Comment by 1as 2 days ago
Comment by johnnyyw 2 days ago
Comment by donnyli 2 days ago
Comment by simjnd 2 days ago
https://github.com/wzhudev/reverse-linear-sync-engine/blob/m...
Comment by dang 2 days ago
Reverse engineering of Linear's sync engine - https://news.ycombinator.com/item?id=44123131 - May 2025 (33 comments)
Comment by throwaway7783 2 days ago
Comment by HoyaSaxa 2 days ago
Comment by SenHeng 2 days ago
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.
Comment by iririririr 2 days ago
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.
Comment by SkyPuncher 2 days ago
It's really the only way for companies to survive and go up market, unfortunately.
Comment by popcorncowboy 1 day ago
Comment by majormajor 2 days ago
"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.
Comment by ryukoposting 2 days ago
Comment by desireco42 2 days ago
Comment by SkitterKherpi 2 days ago
Comment by cyberax 2 days ago
For example, the search field only shows if you press "ctrl-f".
Comment by lenkite 2 days ago
Somehow the web-sites AI generates and the websites an experienced human manually creates are very different.
Comment by smt88 2 days ago
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.
Comment by majormajor 2 days ago
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.
Comment by cyberax 2 days ago
Typically it goes like this: you make a good UI that users like and release your product. The product manager gets rightfully promoted and starts working on other stuff.
A new manager comes in, and they can't really do much. The UI is already close to perfect, after all. So they do a major UI overhaul that inevitably removes/hides functionality and makes things worse.
Comment by nailer 2 days ago
Comment by realusername 1 day ago
I would still say that it's the only SPA I've ever experienced which I'd call "acceptable" and that says a lot on what you need to consider when picking a new stack.
Comment by saagarjha 2 days ago
Comment by seabombs 2 days ago
Comment by cuechan 2 days ago
300ms seems like a lot. Even if this includes the whole http request+response it still seems unbelievably long.
Comment by jpollock 2 days ago
RTT from Hyderabad to the East Coast USA is ~300ms.
Then you have execution and database retrieval.
Comment by IshKebab 2 days ago
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.
Comment by wiseowise 2 days ago
Comment by subygan 2 days ago
but the benchmark is similar software like JIRA, which takes agonizingly long to do anything reasonable.
Comment by Keyframe 2 days ago
As for optimistic routes and "fast" - maybe we ought to talk gmail first?
Comment by spectraldrift 2 days ago
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!)
Comment by Escapado 2 days ago
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.
Comment by hamandcheese 2 days ago
Comment by 12_throw_away 2 days ago
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)
Comment by aboodman 1 day ago
https://zero.rocicorp.dev/docs/connection
Errors and connection are handled in a centralized place so that they automatically get applied to all paths.
Errors immediately disconnect the app and trigger UI. Writes are no longer accepted. After 1 minute of of failed connection attempts, same happens.
This formalizes and enforces the common pattern in popular sync-based apps of detecting disconnects fairly aggressively and warning the user.
Comment by jack_pp 2 days ago
Comment by Escapado 2 days ago
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.
Comment by jack_pp 2 days ago
Warn the user that if they leave the website their changes won't be saved remotely.
Comment by branko_d 2 days ago
Our application uses classical "foreground update" paradigm, but each API call automatically shows an error to the user and returns him/her to the same place where error originated to fix the input and/or retry. As a bonus, we also automatically show progress indicator for any HTTP request that takes more than 1s (which is rare).
This is as simple as:
await context.api.explorer.rename(
{
objectKey,
name
}
);
// objectKey: ApiTypes.ObjectKey
// name: string
The above initiates an HTTP request:- If that request succeeds, you know that the new name has been durably committed to the database.
- If it fails because the new name is not unique, the user sees the error, and can then enter a different name before retrying. The key is that the UI context is preserved during API failure, so everything the user had entered is still on the screen.
- If it takes more than 1s, the user sees a progress indicator which he/she can click to cancel the operation. This also returns him/her to the original UI.
The magic is in the `rename` itself - it was auto-generated from our back-end API such that it wires into our error reporting and progress UI.
Comment by hamandcheese 2 days ago
Comment by michaelchisari 2 days ago
Exhaust all other optimizations before lying to your users about what just happened.
Comment by MaoSYJ 2 days ago
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.
Comment by armdave 1 day ago
Comment by qudat 2 days ago
Comment by jeffbee 2 days ago
Comment by wbobeirne 2 days ago
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.
Comment by mattmatters 2 days ago
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.
Comment by epgui 2 days ago
Comment by esafak 2 days ago
Comment by tnelsond4 2 days ago
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.
Comment by syspec 2 days ago
Comment by mohsen1 2 days ago
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
Comment by nine_k 2 days ago
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.
Comment by blauditore 2 days ago
More generally: You can't circument the trade-offs of a distributed database, which such products are, conceptually.
Comment by ifwinterco 2 days ago
Main downside is it significantly complicates the front end code compared to just waiting for FE to sync with BE before updating
Comment by galaxyLogic 2 days ago
Comment by cwillu 2 days ago
Comment by galaxyLogic 1 day ago
Comment by Onavo 2 days ago
Comment by pyrolistical 2 days ago
Comment by dbbk 2 days ago
Comment by anotherevan 2 days ago
Comment by NewsaHackO 2 days ago
Comment by inezk 2 days ago
Comment by SoftTalker 2 days ago
Comment by aboodman 2 days ago
Make a mutation to what?
The classic server rendered web-app doesn't have any data to make a mutation to. You could try to patch the UI but that would be a huge pita and not really a scalable (in effort) solution.
If you have an SPA, you still don't really have data on the client-side. You have a bunch of cached query responses. You can update those, but (a) it will be a pita to do correctly, (b) you'll have to do it to every possibly affected query, and (c) you have to remember to undo it at the right time (way more subtle than it appears - think it through!).
A sync engine creates the client-side normalized datastore that allows you to "do a mutation client side". In fact, you're kind of right that once you have a sync engine, just doing a mutation is really easy. The real challenge is all the infra required to enable you to do so.
Comment by Aeolun 2 days ago
Comment by willsmith72 2 days ago
Comment by prerok 2 days ago
Comment by artman 2 days ago
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.
Comment by porridgeraisin 2 days ago
Comment by singron 2 days ago
Comment by artman 2 days ago
Comment by porridgeraisin 2 days ago
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.
Comment by cwillu 2 days ago
‹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.”
Comment by benhurmarcel 1 day ago
Do you lose your data then? Or are you thrown back into the form after it closed?
Comment by n_e 2 days ago
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.
Comment by ifwinterco 2 days ago
So no violation of CAP theorem it just prioritises liveness over consistency
Comment by cwillu 2 days ago
Comment by pier25 2 days ago
Comment by WhyNotHugo 2 days ago
The reply with be delayed by days or weeks, but the UI indicated that it had been properly saved.
Comment by pier25 2 days ago
There's not. Browsers can delete "persistent" storage at any time.
https://developer.mozilla.org/en-US/docs/Web/API/Storage_API...
Comment by 12_throw_away 2 days ago
> 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.
Comment by ifwinterco 2 days ago
You can never truly trust anything about a client because by definition you don’t control it
Comment by killingtime74 2 days ago
Comment by aforwardslash 2 days ago
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.
Comment by ifwinterco 2 days ago
Comment by girvo 2 days ago
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)
Comment by cheema33 2 days ago
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.
Comment by girvo 2 days ago
But I’ve only done that in toy examples not prod, I’m sure there’s something I’m missing haha
Comment by sneak 2 days ago
Comment by amelius 2 days ago
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.
Comment by scary-size 2 days ago
Comment by ianberdin 2 days ago
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.
Comment by ianberdin 2 days ago
Comment by lampe3 2 days ago
Comment by echoangle 2 days ago
Comment by ianberdin 2 days ago
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.
Comment by lampe3 2 days ago
Comment by purple-leafy 2 days ago
For context I’ve been obsessed with performance with this game. Prior to this weekend I was struggling to simulate 128 simultaneous players (pathfinding, heavy strategy logic, rendering, all in viewport) and maintaining 120fps on an m1 MacBook Pro - I’d get a very occasional frame drop and my performance was sitting at around 4ms frame times
During the weekend I did very heavy-handed performance sessions and can now simulate 2048 simultaneous players with sub millisecond frame times - that includes all rendering and all logic and includes things like proc gen too.
Further - I’ve been simulating a throttled device with 11.2x cpu throttling (a potato / beet low end mobile devices) and can get stable 60fps at around 256-512 simultaneous players and around 5ms frame times.
My main culprits now are some slight logic issues and startup / boot times which I need to improve on potato devices. I reckon I could take some learnings from linear.
Comment by TheHiddenSun 1 day ago
Specifically targeting your use case with high fps.
Downside: db lives in memory and has to be stored separately for long term use (as of now, may change laterl
Comment by lampe3 2 days ago
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.
Comment by 255kb 2 days ago
And it was doing exactly what is described here, using a reimplementation of mongodb, in the frontend: minimongo.
Comment by yturijea 1 day ago
However this also brings back to the point of why would we expensive html page compared to a small app? (The question is obvious that it is portability and also the accessability of just accessing a link). - And this here we could start to think about instead of continuing to rely on HTML, JS and CSS, alternatives could be invented, that be much more efficient and powerful.
Comment by wasmperson 2 days ago
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.
Comment by artman 2 days ago
Comment by epolanski 2 days ago
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.
Comment by ggambetta 2 days ago
Comment by pier25 2 days ago
Comment by devnull3 1 day ago
Lack of native encryption in indexedDB is a dealbreaker for certain use-cases.
Comment by matharmin 1 day ago
You do still need a secure key to use with this. The simplest is to persist the key server-side (and specifically not on the client), and provide it to the JS after signing in. If you need to support a completely offline PWA you need something else, e.g. prompting the user for a passcode each load.
Comment by herpdyderp 2 days ago
Comment by mpalmer 2 days ago
Comment by jwr 2 days ago
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.
Comment by epolanski 2 days ago
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.
Comment by apsurd 2 days ago
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.)
Comment by shivekkhurana 1 day ago
Comment by seaal 2 days ago
https://performance.dev/skills
https://performance.dev/how-is-linear-so-fast-a-technical-br...
Comment by nilirl 2 days ago
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.
Comment by ambicapter 2 days ago
Comment by yolo3000 2 days ago
Comment by apothegm 2 days ago
Comment by goosejuice 2 days ago
Comment by apothegm 1 day ago
But even if the admin side is baroque, for a user just dealing with tickets and projects/epics it’s far less confusing.
Comment by andersonklando 2 days ago
A leader of 6 will spend a lot of time in such an app, so the UX is valuable and a differentiator for them.
Comment by mrbombastic 2 days ago
Comment by stackedinserter 1 day ago
Comment by andersmurphy 2 days ago
Comment by tancop 2 days ago
Comment by andersmurphy 2 days ago
This also makes them much more vulnerable to a data leak/breach if their device gets compromised or stolen as the data is all on their device.
The client having access to only what it needs in terms of data and making that as ephemeral as possible is a big part of defence in depth.
Comment by sscaryterry 1 day ago
Now hooked on https://github.com/schacon/ticgit
Comment by adverbly 2 days ago
https://github.com/tinyplex/tinybase seems kinda good maybe?
Comment by edward28 1 day ago
Comment by artman 2 days ago
Comment by herpdyderp 2 days ago
Comment by victor9000 2 days ago
Comment by tehwebguy 1 day ago
Comment by let_rec 1 day ago
What if this happens?
1. User makes a mutation
2. UI updates instantly
3. User closes the app before sync happens
4. User comes back and is surprised to see that their mutation did not actually happen
The loading / error / success states in a UI serve a purpose.
Comment by Liquid_Fire 1 day ago
I assume you would also have some sort of icon that indicates all of the changes you made have been synced (or the opposite, an icon for when they haven't yet).
Though of course, it doesn't help if the user outright kills the app or pulls the plug or whatever. But I think in that case they should expect to lose some data?
Comment by ArcaneMoose 2 days ago
Comment by greatgib 2 days ago
Comment by AG342 2 days ago
Comment by einpoklum 2 days ago
Comment by manoDev 2 days ago
Comment by faangguyindia 2 days ago
It's blazingly fast approach.
Comment by jonny_eh 2 days ago
Comment by owentbrown 1 day ago
Comment by ralferoo 2 days ago
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.
Comment by ozgrakkurt 2 days ago
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
Comment by fsuts 2 days ago
Do you mean USA and other developed countries? Or the world
Comment by ozgrakkurt 2 days ago
Comment by zawaideh 2 days ago
Comment by cheema33 2 days ago
Linear is based in San Francisco. And has offices in New York and Finland. Which one of these is an apartheid state?
Comment by zawaideh 1 day ago
Comment by stackedinserter 1 day ago
Comment by zawaideh 1 day ago
Comment by rsingel 2 days ago
Comment by sevenseacat 1 day ago
Comment by blauditore 2 days ago
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").
Comment by ArtRichards 2 days ago
Comment by xiaoyu2006 2 days ago
Comment by joeyguerra 2 days ago
Comment by 0xbadcafebee 2 days ago
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
Comment by throwaway7783 2 days ago
Comment by callamdelaney 2 days ago
Comment by trick-or-treat 2 days ago
Comment by lolmaowutfa 2 days ago
Comment by fdegmecic 1 day ago
Comment by bfung 2 days ago
Feels like AI slop.
Doesn’t address the concurrent update problem except for “optimistic”. At least provide some data why that’s ok.
Comment by tibbon 2 days ago
Comment by repeekad 2 days ago
Comment by frictasolver 2 days ago
Comment by qcautomation 2 days ago
Comment by rubenvanwyk 2 days ago
Comment by marqueandrepris 1 day ago
Comment by zubairismail 1 day ago
Comment by sieabahlpark 2 days ago
Comment by MagicMoonlight 2 days ago
Comment by victorkulla 2 days ago