Rendered at 23:11:47 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
ricardobeat 1 days ago [-]
I would always hear about how Linear was fast, but after actually working daily with it, I’ve lost enthusiasm. Search is quite slow, the UI is often clunky (looks good though), “Pulse” is a torrent of noise even at small scale, and I have trouble finding things I need and resort to adding everything to favorites.
Early days’ Trello was the best project tracking experience by far.
Aurornis 10 hours ago [-]
I’ve been underwhelmed, too.
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.
mgdev 2 hours ago [-]
> Whatever they did to market the product
Not-JIRA + dark mode + usable APIs will take you far.
s08148692 13 hours ago [-]
I've used it for a couple of years now and honestly the hype around it is really unjustified. It does the job but it's nothing special. I could go back to Jira and see no change in productivity after a brief re-orientation period
port11 13 hours ago [-]
The comparison to Jira is unfair, Jira is known for abysmal performance.
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.
blanched 10 hours ago [-]
I think it depends on your job/role. Unless you're spending literally all day in Jira, I don't think the performance (or even more broadly, the enjoyment) of one's task tracker affects productivity that much.
theshrike79 12 hours ago [-]
Self-hosted Jira _might_ be usable.
The Atlassian shared hosting one is a nightmare.
difu_disciple 4 hours ago [-]
Self-hosted jira is prone to death by a thousand cuts making it a much worse alternative to the hosted version.
stronglikedan 9 hours ago [-]
I'm sorry but Linear is objectively better than Jira, and it's not even close.
stevoski 15 hours ago [-]
Linear was simple and fast. Unfortunately it is has become an example of creeping complexity.
They keep adding new features at a steady pace, each slowly making the UI more confusing.
12 hours ago [-]
kardianos 11 hours ago [-]
I tried linear demo and many other task tracker/issue trackers. Many of them were just that: "fine" but then wanted a large pricing increase for OIDC connectors or similar.
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.
tipiirai 18 hours ago [-]
Same. And the UI is confusing. The core functionality is buried under the many features I don't need.
nolander 20 hours ago [-]
I love when I try to pull up a ticket during a meeting or a huddle and then awkwardly watch it loading/caching or w/e its doing for an inordinate amount of time.
mb2100 12 hours ago [-]
right? or give me GitHub issues before the React rewrite – at least they loaded fast...
stevekemp 16 hours ago [-]
I found middle-clicking to open a link in a new tab was often fragile.
I'd get 10+ tabs all stuck in a "loading" state, and even force-reloading wouldn't make their contents match the address-bar.
mb2100 12 hours ago [-]
the achilles heel of SPAs everywhere.
mikkelam 14 hours ago [-]
Linear is probably starting to hit enshittification territory. The feature bload is immense and they are trying extremely hard to not be left behind on the AI wave.
20k 17 hours ago [-]
Trello is still my goto for everything. I sincerely hope that it doesn't get enshittified
benjaminwootton 11 hours ago [-]
You can vibe code a clone of Trello in 30 minutes.
20k 8 hours ago [-]
Sure, but I'd like it to be good
jacobgold 1 days ago [-]
These kinds of local-first syncing web apps are really interesting and can be really useful, but I think the premise is somewhat wrong.
"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.
evantbyrne 1 days ago [-]
Thank you. Was beginning to feel like I was taking crazy pills seeing people claim that 300ms is fast when 30ms has been the target TTFB for as long as I can remember. Maybe Linear takes more time on the backend for totally valid reasons and it needs some help from the frontend, but that's not generalizable, and every bit of JS comes with its own costs.
SoftTalker 1 days ago [-]
When I started in this business, "sub-second" responses were the goal.
lmm 1 days ago [-]
> it’s very possible to run a web app backend within ~10ms RTT of most users
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)
aforwardslash 22 hours ago [-]
10 years ago, I was headlining a project where we had strict performance requirements (ssr, php mind you) - the target for any operation except login was 30ms, and any endpoint taking more than 60ms in one of the devs server would have to have explicit approval. Add the nework RTT if you dont want to have a local backend, and for most geographies is still well under 300ms. Fun fact, we actually designed a system that could be easily replicated between regions and/or perform internal routing, leveraging the operator network. 3 AWS regions would make the RTT of a request well within <100ms on average for >80% of the world population. Requests were mostly "instant" - the big trick that did it (at the time) as to avoid reflow in the browser. Funny how the spotlight is now on doing stuff that "solves" that problem with the technology that was designed to solve that problem.
camgunz 13 hours 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.
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.
aboodman 23 hours ago [-]
> 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.
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.
jitl 22 hours ago [-]
you can home tenants in a data center close to them, run a copy of your app in each region including the datastore. keep a central db for accounts, billing, etc but user content is easy enough to shard regionally.
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.
lucaspiller 20 hours ago [-]
This breaks down when someone goes on holiday to Greece for a week, and the RTT over the airbnb wifi is 5 seconds.
Optimistic updates on the frontend are probably simpler too.
jitl 10 hours ago [-]
oh for sure you start with client side cache & optimistic updates. but u need low latency / regional backend for multiplayer to feel good. I didn’t realize who i was replying to, aaron is probably one of the few people who think about sync engines more than me. anyways we do both at notion and of course we did local cache first client way before we did multi region at Notion.
aboodman 22 hours ago [-]
But this is kind of meaningless unless the tenants themselves are in one geo. Take linear as an example, this strategy works as long as your company that uses linear is all colocated in one area. As soon as you have remote people it falls apart.
nijave 3 hours ago [-]
Not necessarily. You can do async replication either at the app level or DB level to other regions
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
aboodman 3 hours ago [-]
What you are describing is exactly what sync engines do. You can have replicas on the server or replicas on the clients. The tradeoffs are the same except the client-based replicas can be in memory, accessed synchronously directly on the ui thread. No server latency at all.
bastawhiz 22 hours ago [-]
But it does mean you gracefully degrade so the majority of the company sees the target latency <100ms and the rest of the company sees "not geo-optimized" latency.
aboodman 15 hours ago [-]
Only in the case where there is such a majority of the company that is tightly geolocated.
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.
jacobgold 7 hours ago [-]
"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."
You're assuming a single global database, which ignores the many alternatives.
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.
andersmurphy 17 hours ago [-]
You don't even need your backend that close if your server is fast enough. Streaming HTML immediate mode is pretty good. See this demo (server is in Germany and runs on a potato uses no optimistic updates, eveb scroll round trips) [1]
Honestly client side animations go a long way to masking latency too.
You can locate an "intermediary backend" on the client, write outstanding mutations into local storage, have a background worker send it to the backend, with necessary retries, etc. At worst, the background worker would put out a message about a failed update which the UI tread would receive and show.
But the happy path stays lightning-fast.
jacobgold 1 days ago [-]
Sure but there's a ton of complexity in any kind of local-first syncing solution. Often the solution is CRDTs.
My point above is that the simple solution ("traditional CRUD app") is actually viable even when the goal is very low latency.
throwaway7783 1 days ago [-]
100% agree. A traditional CRUD app (like Linear) can be made pretty low latency without local-first. The complexity is not worth it.
nine_k 24 hours ago [-]
How would you do it? The speed of light being what it is, and tail latencies being large unless you over provision wildly, you keep facing hundreds of milliseconds of reconciliation time, if you want guaranteed synchronous result.
throwaway7783 22 hours ago [-]
I said it in another thread. Yes, of course we cannot match the local store interactivity. What I mean is coast to coast RTT is 65-80ms. And the server can be optimized to return back basic operations (adding a comment, a new ticket, reading one back etc) can be done within tens of milliseconds, keeping the entire thing under 100ms coast-to-coast. If one can colocate servers with users, it becomes even less. I'd rather to server side "reconciliation" than do both (because serverside thing doesnt go away even with localstore, it is just deferred).
1 days ago [-]
pastel8739 1 days ago [-]
Can you do this where you need to have a database shared between all these edge backends?
bastawhiz 22 hours ago [-]
If your service shares state globally across all users (like a social network) not really. If individual customers are mostly centered around one geographic location and their data isn't shared with other customers, yes.
swiftcoder 13 hours ago [-]
Even social networks tend to shard their DBs geographically. It's obviously not 100% effective (I can always add a friend on the other side of the world), but it significantly improves the average case (the majority of my friends will have gone to school/university/worked in the same geographical locations I did)
aboodman 23 hours ago [-]
If you're interested in this kind of experience for your application, check out Zero (https://zero.rocicorp.dev/).
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.
thingortwo 18 hours ago [-]
this is not local/offline first and also seems massively over-engineered for the type of apps that might use it since now you can't use plain sql and need to learn your ZQL domain specific language/library. I mean look at this code from raw sql example:
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]
)
}
}
)
```
aboodman 17 hours ago [-]
yeah we specifically decided not to be local/offline-first because these add huge complexity that is not needed for the type of apps we want to support. I spoke about this a bit here if you are interested: https://www.youtube.com/watch?v=86NmEerklTs&t=1764s
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.
thingortwo 16 hours ago [-]
Most of the improvement opportunity is in the offline-first the cache or fast reads already have lots of solutions by using zero I lock my self in thinking in your library's design language and not in terms of what I already know that is raw SQL. If your happy/convenient/recommended path is ZQL then of course people will choose it and only later realize it only works for simpler queries like most ORMs I've been burnt by prisma before and now I don't touch any ORMs and simply use raw sql + sqlc (type safety + auto repo layer from your query.sql)
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.
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.
Which is exactly why I said this problem is better solved by your own custom sync engine which when scoped to your own problem is not as hard to build as you think the most simpler and effective version is just a change_log and replaying or occasional full sync.
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.
kobieps 10 hours ago [-]
Did you look at PowerSync? It's "just" SQLite on the client. Wound be curious to hear what you think of it in the context of your lock in concerns
1as 21 hours ago [-]
Can't recommend Zero enough. It's a really thoughtful piece of software with great abstractions. As well as the obvious performance benefits arising from having data synced and available in the client, I've been blown away by how much it simplifies React code. With a sync engine in place, most client-side state seems to evaporate, you get to think synchronously in most component code, etc.
johnnyyw 22 hours ago [-]
We've been using Zero for a while now. Originally we were looking to build a Linear sync engine in-house and came across Zero. Probably the closest thing you can get without staffing a team dedicated on this.
donnyli 22 hours ago [-]
user as well; this is exactly the tool if you want a UX where the UI instantly update itself once database changes within milliseconds.
simjnd 1 days ago [-]
Last year a guy reverse engineered Linear's sync engine and published it on GitHub with a cool explainer.
Writing an eventually consistent database is hard, it maybe fine for Linear's use cases, but not knowing if my updates made it to the server (aka my team),is problematic. The sync lags have created untold problems in other projects I have worked for, so I always go for a synchronous solution. All the fancy stuff comes out only if it's absolutely needed. I'd rather optimize my server to be blazing fast, and have the user "suffer" network latency.
booi 1 days ago [-]
Yeah I’ve had inconsistencies before with linear but jira is a garbage fire so…
angrydev 1 days ago [-]
This is my experience but haven’t used linear in 2 years. Switching to JIRA was miserable.
armdave 7 hours ago [-]
"The server is a target for syncing rather than the source of truth." For the type of problem Linear is trying to solve - a task/issue tracker - this makes sense. But I don't think it's the correct mental for web apps where the main concern is transactions/orders. The server is the SoT, or else you will deal with all sorts of crazy reconciliation scenarios that make for a lousy user experience (ex: the app accepted payment and promised an order, but in reality there is no inventory, which the server would have known had we checked there first).
HoyaSaxa 1 days ago [-]
We use Linear at work. I’m definitely in the minority, but I really struggle with the UX. I also wouldn’t call it fast. Sure the page technically loads reasonably quickly, but half the time I see numbers updating on the page with no visual indicator that data loading is still happening.
SenHeng 1 days ago [-]
A common problem I have with Linear is that repeated writes sometimes overwrite themselves. Consider this flow
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.
iririririr 23 hours ago [-]
their sign up also failed me. probably because i used +
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.
SkyPuncher 1 days ago [-]
Linear has become the thing it sought to kill - complex.
It's really the only way for companies to survive and go up market, unfortunately.
popcorncowboy 3 hours ago [-]
The only way for companies _that raise nine figures chasing decacorn status_ to survive
majormajor 23 hours ago [-]
I don't use Chrome, so I don't know if that has something to do with it, but Linear pages often hang or take many many seconds for first load.
"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.
ryukoposting 23 hours ago [-]
I had a previous employer switch from Linear to Jira because of the wack UX. Lots of icons with no clear meaning, poor discoverability, and yeah, stuff changing on the page with seemingly no indication for why.
realusername 8 hours ago [-]
I'm also in the same boat, I struggle with the UX and get lost frequently.
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.
desireco42 1 days ago [-]
I share your sentiment, not to say that Linear is bad, just that I don't see it as good UX or fast.
nailer 1 days ago [-]
God it’s awful. I had to get a colleague to show me how to add a due date to an item because it’s hidden in the nav pane. It’s better than Jira, but that’s a very low bar.
cyberax 1 days ago [-]
Linear enshittified their UI in the name of "clarity". You know the drill: remove functionality, add small icons with invisible text and HUGE padding, hide controls, etc.
For example, the search field only shows if you press "ctrl-f".
lenkite 15 hours ago [-]
This pretty much sounds like the result of AI coding. No - I am not being facetious. This is exactly how AI generates websites like an unergonomic stamp quite contrary to a way an experienced CSS/UX designer would do.
Somehow the web-sites AI generates and the websites an experienced human manually creates are very different.
smt88 24 hours ago [-]
I wouldn’t call it enshittification because there’s no business purpose to it.
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.
majormajor 23 hours ago [-]
Apple generally makes it hard to do non-default things, but "basic" functionality is usually evident. E.g. it's not hard to delete a photo in the iPhone photos app; but it's multiple taps to get to manual edit options through a non-obvious-to-newcomers "sliders" button first and then a pen button on the top side of the screen (whereas the first edit button was on the bottom). Advanced stuff isn't discoverable, basic stuff generally is (there are exceptions, of course).
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.
cyberax 23 hours ago [-]
Yeah, agreed. We need another word for that. Literally everybody in the industry is affected by this.
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.
SkitterKherpi 1 days ago [-]
[dead]
saagarjha 1 days ago [-]
I’ve actually found Linear to be quite slow? There was a week where it would also spin at 100% CPU if I left a tab with it open for a while :(
seabombs 1 days ago [-]
It appears to use a lot of memory too, at least in Firefox. I can't have more than a few Linear tabs open at a time.
cuechan 1 days ago [-]
> A traditional CRUD app doing the same thing takes about 300ms.
300ms seems like a lot. Even if this includes the whole http request+response it still seems unbelievably long.
jpollock 1 days ago [-]
It depends on where your clients are, and where your servers are. You may have a lot of customers in a country and not want to host servers in that country (tax, regulatory, maintenance cost, etc.)
RTT from Hyderabad to the East Coast USA is ~300ms.
this is kinda missing the point. yes, the raw http calls __should__ be enough for 300ms.
but the benchmark is similar software like JIRA, which takes agonizingly long to do anything reasonable.
IshKebab 1 days ago [-]
Yeah you can easily get this speed down to like 100ms which is near enough to instant to not matter. You definitely don't need to go to client side rendering for most things to feel fast.
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.
Keyframe 1 days ago [-]
This is neat. To be honest, I never considered Linear as "fast". Seemed laggy as most web apps, but in contrast to JIRA it's lightspeed of course. Linear is great though, a real refreshment after JIRA torture.
As for optimistic routes and "fast" - maybe we ought to talk gmail first?
spectraldrift 23 hours ago [-]
Linear seems like the kind of app that typically wouldn't need this sort of optimization. I could understand local copy of the DB for indexing, perhaps, but not for normal transactions. This app seems like an example of an app that would typically scale well on the backend - concurrent writes for a single issue would typically be rare, and if sharding is necessary for large customers it should shard quite well.
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!)
Escapado 1 days ago [-]
I love using linear and where applicable I try to use optimistic updates in the applications I build too and I think it's a good article that touches on the most important aspects of a performant web app of that caliber.
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.
hamandcheese 1 days ago [-]
I worry that optimistic updates is going to become trendy and applied to more software, but without any plan for the "sad path" - failed to sync, sync conflict, etc. Get ready for a whole new era of race conditions and frustration!
aboodman 7 hours ago [-]
Yeah, it's a big part of the tradeoff for this kind of architecture. In Zero, this is a first-class concern via the ConnectionStatus API:
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.
12_throw_away 1 days ago [-]
> optimistic updates [...] without any plan for the "sad path"
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)
jack_pp 1 days ago [-]
Don't you have the same problems with basic CRUD apps? Also you need to handle the sad path for every single request instead of having the sync engine do it all in one place.
Escapado 1 days ago [-]
Correct but the feedback is usually more immediate. Save a change to your issue and it fails - You will get an error toast and probably stay on the form.
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.
jack_pp 1 days ago [-]
Well the sync engine can figure out if there's an issue fast, say <500ms, if you build it that way. Then you can just make a toast telling the user there are issues and anything they do will be saved locally only for the time being.
Warn the user that if they leave the website their changes won't be saved remotely.
artman 1 days ago [-]
In reality conflicts almost never happen.
Escapado 16 hours ago [-]
I agree. I just argue that you would still want your app to handle those with as little data loss as possible and in a way that's understandable to the user.
branko_d 16 hours ago [-]
You can have it in one place without using background sync.
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).
- 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.
hamandcheese 1 days ago [-]
The issue that I foresee is that the point of error becomes decoupled from the UI and the UI doesn't handle a delayed error. Especially if retrofit into existing products.
michaelchisari 24 hours ago [-]
The complexity required of a basic CRUD app versus a client-side optimistic update are worlds apart.
Exhaust all other optimizations before lying to your users about what just happened.
MaoSYJ 1 days ago [-]
well it has been the standard for non critical feedback.
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.
qudat 24 hours ago [-]
Preload is the answer to speed. Basically download the clients db on init and then have cache invalidation strategies. I built starfx to basically perform the data sync aspect of this paradigm https://starfx.bower.sh/learn#data-loading-strategy-stale-wh...
wbobeirne 1 days ago [-]
I'm glad to see more positive takes on client-side rendering. Unless SEO and crawlability are important for your site, server side rendering is such an overprescribed solution. If I put my tin-foil hat on, I'd say it gets a lot of attention because it's a lot easier to charge people for server time spent in SSR that you just don't have in CSR.
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.
mattmatters 1 days ago [-]
Linear still only uses j/k for up and down. I've asked over and over for the past 5 years for ctrl+n/ctrl+p up and down and have gotten nonsensical answers from support.
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.
epgui 1 days ago [-]
I’ve never thought of Linear as particularly fast.
esafak 1 days ago [-]
What web app would you put forward instead?
epgui 7 hours ago [-]
I wouldn’t.
scary-size 16 hours ago [-]
It's so satisfying to see all these techniques applied to a product. There's nothing really new tech-wise. Optimistic updates were shown in a lot of early React demos more than ten years ago. Bundle splitting, preloading and service workers have been around for a long time. But it does take a huge amount of rigor and determination to apply these methods in each layer.
yturijea 14 hours ago [-]
I think the interesting thing that made a small "aha" for me reading this was that: This is a direct parallel to an application on your PC or any other program you would install.
1. They do Client Side Rendering(CSR)
2. they have client side database
3. they sync asynchronous to to the server (or atleast some do, and other have direct manual action for it)
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.
tnelsond4 23 hours ago [-]
If it uses indexedb under the hood, indexedb is ridiculously slow on first write but subsequent loads are very fast, if you're loading data you would have to load it via some other method first before putting it in indexedb, in order to make it fast.
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.
purple-leafy 21 hours ago [-]
This is cool! Maybe I can incorporate something similar into the browser game and engine I’m developing to remove load states entirely after first load? My thing is fully client side static assets no server.
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.
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
syspec 1 days ago [-]
Whole blog post is basically: Make a mutation in the clientside, assume it worked, and save in the background.
mohsen1 1 days ago [-]
Works for Linear because the tab stays open, and worse case if tab is closed you can recover later when the tab is opened again and deal with conflict resolution. Won't work if:
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
nine_k 1 days ago [-]
The user clicks the button, the mutation is stored in local storage. The user closes the tab, but it's not a problem.
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.
blauditore 1 days ago [-]
1. What if the browser gets closed/killed?
2. Error messages around syncing issues are notoriously worse than those of a sync request to the backend that failed. So the UX in the end is worse.
More generally: You can't circument the trade-offs of a distributed database, which such products are, conceptually.
ifwinterco 1 days ago [-]
Yeah this pattern can be made to work fine.
Main downside is it significantly complicates the front end code compared to just waiting for FE to sync with BE before updating
galaxyLogic 1 days ago [-]
What if we had a local server running on the same PC, which then relays the request to some shared server on the internet?
cwillu 1 days ago [-]
That's what a background worker is: a local server managed by the browser and only accessible to pages of the origin domain.
pyrolistical 1 days ago [-]
Expose the sync queue to end user and train them to understand if they attempt to close the tab with a pending queue they will get the ugly prompt warning them
dbbk 1 days ago [-]
Still works if you use beacon requests, they survive tab close
anotherevan 24 hours ago [-]
Bear in mind that beacon is something of a hail Mary, though. No way to tell if it was successful or not or react if it wasn't.
Onavo 1 days ago [-]
Yes and for linear (if you break it down strictly in a theoretical CS sense, is roughly equivalent to TodoMVC in terms of application complexity assuming non-collaborative text editing) they have clearly defined states for most items and few if none truly destructive actions. The hardest part is anything text related.
NewsaHackO 1 days ago [-]
I agree. To each their own, but the UI updating automatically doesn't really add much value to me. I would prefer that the view I am seeing is a snapshot in time of what the ground truth server was, not some mixed state that forces me to consider the possibility that seeing my request go through on the screen doesn't actually mean it went through and has been sent to the server.
inezk 1 days ago [-]
The goal is - and I think they have achieved it - is that you don't have to think about it. They handle sync, and they do it reliably.
SoftTalker 1 days ago [-]
Then they have solved one of the fundamental hard things in computer science.
aboodman 23 hours ago [-]
Well that "make a mutation client-side" phrase is doing a lot of work.
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.
Aeolun 24 hours ago [-]
I think whole generations are constantly discovering that the client is really, really fast.
willsmith72 23 hours ago [-]
so.. optimistic ui? like upvotes on hacker news since forever?
prerok 1 days ago [-]
Indeed. I have to say, I hate this. Suppose you are in a meeting, you update something and you see the result, but the rest of the team does not. Ok, a couple of hundred ms does not play into this but if the update does not make it through? And yes, it happens.
artman 1 days ago [-]
Changes go through and synced to everyone on your team in almost realtime. If there's a conflict on the server and your change cannot be applied (almost never happens), your change is rolled back on your client, again, almost in realtime. If servers cannot be reached, we will show you a syncing badge within 4 seconds to tell you that you have made changes that haven't been sent to others yet.
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.
benhurmarcel 9 hours ago [-]
> If there's a conflict on the server and your change cannot be applied, your change is rolled back on your client
Do you lose your data then? Or are you thrown back into the form after it closed?
porridgeraisin 1 days ago [-]
(curious) What if a user closes it before 4 seconds? Ctrl+enter, it optimistically locally updates within 1 second. I close ctrl+w. But my wifi goofed and it didn't reach the server.
singron 1 days ago [-]
I have mysteriously lost comments/descriptions I wrote on issues. I figured it was related to a failed and lost opportunistic update like this, although I suppose it could have been caused by a fixable bug.
artman 1 days ago [-]
The HTTP request is fired off instantly, so chances are that the request is already written to the socket and closing the page won't cancel the request. Should your wifi-router drop it, your client will retain the transaction on disk and retry it the next time you come online.
porridgeraisin 1 days ago [-]
> next time you come online
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.
cwillu 1 days ago [-]
That's really gross behaviour; users like it because they don't understand it and don't know to blame it for their issues when weird things happen to them, and weird things to them all the time.
‹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.”
n_e 1 days ago [-]
As a user, I like when things appear to sync instantly and perfectly, such as in Google Docs.
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.
ifwinterco 1 days ago [-]
In the case of a partition the client nodes get temporarily out of sync but the system will then synchronise to one state again once the partition is resolved if it’s written correctly.
So no violation of CAP theorem it just prioritises liveness over consistency
cwillu 1 days ago [-]
Which is to say: in case of partition, it loses data.
pier25 1 days ago [-]
For native apps this is less of an issue since they have access to persistent storage but with browsers there's no guaranteed persistence.
WhyNotHugo 1 days ago [-]
There's guaranteed persistence, but there's no guarantee that the host will be up anytime soon. E.g.: I might leave a final reply with all the details on an issue before going on vacation (or maybe I don't work the next day but my colleagues abroad do!). I see that it's properly posted and close the laptop.
The reply with be delayed by days or weeks, but the UI indicated that it had been properly saved.
pier25 1 days ago [-]
> There's guaranteed persistence
There's not. Browsers can delete "persistent" storage at any time.
> 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.
ifwinterco 1 days ago [-]
Edge case but playing devil’s advocate: a user can also uninstall the native app at any time, and might still expect their last change before they closed the app to be reflected in the web version.
You can never truly trust anything about a client because by definition you don’t control it
killingtime74 1 days ago [-]
But the os can't uninstall the native app at any time unprompted right
aforwardslash 24 hours ago [-]
Corner case: actually, it can. Also, thats how auto-updating works ; depending on local state as a source of truth using browser apis is a terrible idea IMO.
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.
ifwinterco 1 days ago [-]
No, it’s definitely a lot less likely and probably an edge case you can ignore in practice
girvo 1 days ago [-]
AKA what Relay does out of the box haha
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)
cheema33 23 hours ago [-]
> AKA what Relay does out of the box haha...
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.
girvo 23 hours ago [-]
I think that’s an acceptable trade off, personally… but! You can implement persistent caching if you’d like :) you create the record store when you stand up the environment, so it’s possible to have that hydrated with data from somewhere else
But I’ve only done that in toy examples not prod, I’m sure there’s something I’m missing haha
sneak 1 days ago [-]
I believe this is called “eventual consistency”.
amelius 1 days ago [-]
No, because if a transaction fails it still needs to be handled by the user in many cases.
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.
ianberdin 1 days ago [-]
Thanks for the read. It is a bit more complicated than you think.
I completely rebuilt this sync engine + orm with relations, lazy loading etc, using Vue + Pinia in https://playcode.io. Google Linear’s videos, they explained in detail their architecture.
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.
ianberdin 1 days ago [-]
Feel free to ask questions.
lampe3 1 days ago [-]
what google linear video?
echoangle 1 days ago [-]
I think google is an imperative verb here. You're supposed to google (as in search for) Linears Videos.
These are original video. Very clear and a way better than the blog post.
lampe3 17 hours ago [-]
thanks!
lampe3 1 days ago [-]
funny how things come back.
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.
255kb 1 days ago [-]
Had to scroll this far to see meteors being mentioned.
It was the first framework I started my js journey with.
And it was doing exactly what is described here, using a reimplementation of mongodb, in the frontend: minimongo.
devnull3 15 hours ago [-]
How do these ideas work for domains which need encryption at rest? (e.g Healthcare, fintech, etc)?
Lack of native encryption in indexedDB is a dealbreaker for certain use-cases.
matharmin 10 hours ago [-]
At PowerSync we use a wa-sqlite build with SQLite3MultipleCiphers for encryption at rest.
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.
In gamedev, "optimistic updates" are called "client-side prediction," and are a standard part of multiplayer games. IMO it's somewhat risky to apply the technique to web-apps, since each network request typically corresponds to some important operation, and optimistically updating the UI is lying to the user about whether that operation completed successfully.
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.
artman 1 days ago [-]
Operations are on average applied within a few hundred milliseconds, and almost never fail. Because of this we treat the success path as default, and indicate that your changes haven't been applied only if we detect that you're offline, or if it takes more than 4 second to apply the changes.
I see this comment over and over again, yet I know lots of Linear-enthusiastic people and none is suffering of this.
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.
1 days ago [-]
pier25 1 days ago [-]
Honestly you can POST to an API to the other side of the world and receive a response in less than 300ms. That's literally the blink of an eye. There are very few use cases where you'd need even lower latency than that while at the same time sacrificing reliability (there's no guaranteed persistent storage in a browser).
herpdyderp 1 days ago [-]
My Linear client gets stuck "syncing" all the time, completely blocking all interaction. I'd rather have loading spinners than getting, essentially, locked out of the whole app for 10 minutes.
tehwebguy 13 hours ago [-]
Linear still has broken Back / Forward after literally years! Instead of fixing it they introduced a Pinned Tab feature that doesn’t actually do anything! Absolutely drives me nuts.
let_rec 13 hours ago [-]
Hang on...
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.
Liquid_Fire 8 hours ago [-]
Presumably you could trigger one of those onbeforeunload confirmation dialogs in that case.
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?
andersmurphy 17 hours ago [-]
Sync engines are fast to a point but if you start working with large enough datasets and/or care about security you ultimately end up with something closer to streaming immediate mode HTML. Of course that means sacrificing local first.
tancop 17 hours ago [-]
whats the security problem here? all mutations go through the server authority and clients can only load data they have access to. the only thing i see is users being able to read cached versions of private content that was accidentally set to public then private again, and if that happens to you something is wrong with your access controls. and its also not a big deal for most organizations.
andersmurphy 16 hours ago [-]
User permission can often be very dynamic. Sync engines (local first ones even more so) give them access to a much larger set if that data in a client side database.
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.
jwr 1 days ago [-]
Linear is the best web app I have ever seen, period. It is also the best bug-tracker I've ever used. I use it, and pay for it (gladly). It's worth it.
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.
apsurd 1 days ago [-]
Interestingly, as a professional developer I had the opposite first experience. It's a tracker. It looks like every other tracker. Everything is really small and "clean" which means they just hide everything, for that modern look.
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.)
epolanski 1 days ago [-]
Jira might be a technological disaster I hate with all of my guts, but as a product, it's on a completely different level than Linear.
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.
seaal 1 days ago [-]
Pretty nice how they package each article into a skill, wonder how useful they actually are in real world workflows.
I wish Linear would participate in FedRAMP. It's blocking adoption at my company.
nilirl 1 days ago [-]
Am I the only that doesn't "get" Linear? Even if speed is the killer feature, isn't an issue tracker a relatively low-frequency application for a dev?
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.
ambicapter 1 days ago [-]
I honesty can’t say it’s better than Jira, the myriad options just make it a confusing mess to figure out how to navigate and put stuff (could be my company is just hella disorganized), and the GitHub tracking is annoyingly eager (just because the first PR has been merged doesn’t mean the ticket is done).
yolo3000 1 days ago [-]
Maybe we work at the same company. I have the same experience. Don't know why people use it.
apothegm 1 days ago [-]
Say what you will about Jira, at least it’s not a total maze.
goosejuice 22 hours ago [-]
Guess you've never seen the administrative side?
apothegm 14 hours ago [-]
On the contrary. I’ve been an admin more than not and configured it from scratch 4 or 5 times.
But even if the admin side is baroque, for a user just dealing with tickets and projects/epics it’s far less confusing.
andersonklando 1 days ago [-]
It is definitely way more performant than Jira + Microsoft Planner. Trello comes to mind in terms of performance. I speak from being a heavy user of Linear for my personal project and as a Jira enterprise user.
A leader of 6 will spend a lot of time in such an app, so the UX is valuable and a differentiator for them.
mrbombastic 1 days ago [-]
Jira is fine and devs just grow to hate issue trackers because they hate that part of the job. Watch for another one to come and go when people get sick of linear.
stackedinserter 10 hours ago [-]
I don't understand it either. How can "Jira but faster" be a selling point?
victor9000 1 days ago [-]
The linear MCP is amazing to work with as it lets me keep all my workflows in the terminal. The ergonomics around search and ticket management are dead simple going through a terminal agent, so I didn't need much more convincing after enabling that configuration.
adverbly 1 days ago [-]
Any good resources or wrappers for indexedDB people would recommend? The API seems kinda unapproachable compared to other data stores.
I recently started interacting with JIRA exclusively through AI agents (as a Skill in Cursor) and the difference between Linear and JIRA has immediately evaporated. No UI clunkiness problems if I don't even use it :)
AG342 1 days ago [-]
I've always wondered how they made the UX so slick - thanks for sharing.
manoDev 22 hours ago [-]
Good job – but the article seems to introduce new lingo ("sync engine"?) to describe a well known pattern: optimistic user interface.
faangguyindia 22 hours ago [-]
Well, i also design my apps like this.
Basically, my apps are PWA app which sync data in backend to sqlite+go backend.
It's blazingly fast approach.
jonny_eh 1 days ago [-]
I like it when a spinner represents the truth about the state of the value on the server. I want to know when/if it's been updated.
jeffbee 1 days ago [-]
This is bizarrely laudatory. The app is fast because it is not correct. The user has no way to know if their view is consistent with any other user's view. The user has no way to know if the app silently discarded one of their inputs because of a conflict. Linear developers seem to believe that silent data loss is an acceptable cost for maintaining the illusion of speed.
ralferoo 1 days ago [-]
I've never used linear, but just watching their example video [1] would worry me if I was a user and it's actually doing what they say it's doing.
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?
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.
ozgrakkurt 23 hours ago [-]
Focusing on making things actually fast is always better than putting a cache or some hack on 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
fsuts 22 hours ago [-]
When you say most people have more than 100mb/s, 4 cores and 8gb
Do you mean USA and other developed countries? Or the world
ozgrakkurt 21 hours ago [-]
I mean the people using linear
zawaideh 23 hours ago [-]
It’s really unfortunate that it is built in an apartheid state.
cheema33 23 hours ago [-]
> It’s really unfortunate that it is built in an apartheid state.
Linear is based in San Francisco. And has offices in New York and Finland. Which one of these is an apartheid state?
zawaideh 12 hours ago [-]
You are right. I was confusing it with another company LinearB. I should have double checked.
stackedinserter 10 hours ago [-]
LinearB is not from "apartheid state" either.
zawaideh 7 hours ago [-]
Offices in Tel Aviv including founder from there.
rsingel 1 days ago [-]
I miss Pivotal Tracker
sevenseacat 10 hours ago [-]
oh god I'm not the only one!
blauditore 1 days ago [-]
TL;DR: Background syncing instead of synchronous updates to the cloud.
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").
ArtRichards 1 days ago [-]
They're doing some really cool stuff with Agents too.. automating software pm. it's a huge difference between this and their (unnamed) larger competitors.
xiaoyu2006 1 days ago [-]
I was expecting a post on how hardware accelerate linear algebra lol
mpalmer 1 days ago [-]
Did we need five thousand words (easily 1/3 of which didn't deal with the main topic) on techniques that aren't exclusive to Linear in the slightest?
fdegmecic 11 hours ago [-]
or just use liveview
joeyguerra 1 days ago [-]
just one thing, this is not the simplest stacks you can find.
trick-or-treat 19 hours ago [-]
IMHO indexedDb is almost certainly the wrong choice and whatever perceived speed wins can be accomplished without it and optimistic updates. Prove me wrong
0xbadcafebee 1 days ago [-]
> Literally the first lines of code that I wrote was the sync engine, which is very uncommon to what you usually do when you're a startup
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
throwaway7783 1 days ago [-]
Based on all the comments above, it is very debatable if it is a better product all said and done
callamdelaney 1 days ago [-]
It’s webscale
lolmaowutfa 1 days ago [-]
Interesting.
Looking forward to the next article explaining how it is so slow.
(TL;DR it's a webapp disguised as a desktop app.)
greatgib 23 hours ago [-]
Probably linked to this use of "in-browser" database, but for me, keeping a few linear tabs open for too long, and the memory usage of my web browser skyrocket to abusive levels...
einpoklum 1 days ago [-]
I was expecting a post about how O(n) algorithms can sometimes beat o(n), if you can take advantange of the hardware. Instead I get some mountain of stuff targeting "agents". Bah.
bfung 1 days ago [-]
Meta: so many words to say - save local first & sync in background.
Feels like AI slop.
Doesn’t address the concurrent update problem except for “optimistic”. At least provide some data why that’s ok.
tibbon 24 hours ago [-]
Now… why is Jira so painfully slow? Even changing the type of a ticket or moving it takes like 20 clicks and a minute
repeekad 24 hours ago [-]
Optimistic writes are cool and all, but I'd trade 150ms for search as you type any day.
Early days’ Trello was the best project tracking experience by far.
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.
Not-JIRA + dark mode + usable APIs will take you far.
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.
The Atlassian shared hosting one is a nightmare.
They keep adding new features at a steady pace, each slowly making the UI more confusing.
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.
I'd get 10+ tabs all stuck in a "loading" state, and even force-reloading wouldn't make their contents match the address-bar.
"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.
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)
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.
What are you talking about? The only AWS region < 10ms away from us-east-1 is, err, us-east-2:
https://www.cloudping.co/
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.
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.
Optimistic updates on the frontend are probably simpler too.
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
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.
You're assuming a single global database, which ignores the many alternatives.
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.
Honestly client side animations go a long way to masking latency too.
- [1] https://checkboxes.andersmurphy.com/
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.
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.
```
const markAllAsRead = defineMutator( z.object({ userId: z.string() }), async ({tx, args: {userId}}) => { // shared stuff ...
)```
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:
```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.
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.
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!
https://youtu.be/SNAHZZo21To?si=wgDgxQpbRr-qj-A-&t=1571
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.
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)
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.
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.
For example, the search field only shows if you press "ctrl-f".
Somehow the web-sites AI generates and the websites an experienced human manually creates are very different.
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.
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.
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.
https://wondernetwork.com/pings
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.
As for optimistic routes and "fast" - maybe we ought to talk gmail first?
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!)
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.
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.
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.
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:
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.
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.
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.
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.
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.
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
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
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.
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.
Do you lose your data then? Or are you thrown back into the form after it closed?
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.”
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
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.
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
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.
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.
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.
Lack of native encryption in indexedDB is a dealbreaker for certain use-cases.
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.
Now hooked on https://github.com/schacon/ticgit
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.
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.
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?
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.
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.
https://performance.dev/skills
https://performance.dev/how-is-linear-so-fast-a-technical-br...
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.
But even if the admin side is baroque, for a user just dealing with tickets and projects/epics it’s far less confusing.
A leader of 6 will spend a lot of time in such an app, so the UX is valuable and a differentiator for them.
https://github.com/tinyplex/tinybase seems kinda good maybe?
It's blazingly fast approach.
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.
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
Do you mean USA and other developed countries? Or the world
Linear is based in San Francisco. And has offices in New York and Finland. Which one of these is an apartheid state?
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 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.