![]() |
|
![]() |
| Ha. I just realized it is 10 years since me, Alexei and Oleg launched our CRDT demo on HN. Funnily enough, it featured this mouse-cursor trick as well. It was a demo of the lib. It was August 2014 if I remember correctly. The post was submitted by Dan Abramov. Nostalgie.
That repo is long abandoned https://github.com/gritzko/swarm Did CRDTs and local-first advance much since 2014? There is a bunch of research, way more CRDT libs on GitHub, but (somewhat) successful businesses you can count on one hand. I guess, that is the actual challenge - figure out the business model. |
![]() |
| Yep. Local-first is further commoditizing software.
Users will refrain from paying a subscription if their app version works and they don't need updates. The good old buy-a-license with 1 year of updates will likely make a big comeback. Local-first software, at least in lix universe [0], does not have fixed recurring maintenance costs like running servers. The elimination of fixed recurring maintenance costs will lead to a proliferation of "hobby apps" that are undercut by price/make it free altogether. [0] https://github.com/opral/monorepo/blob/main/lix/README.md |
![]() |
| I've mostly been cloud free this way. I do send (sometimes encrypted) backups/copies elsewhere, but those are exceptions. And it works well across many client devices (and remote via DNS/forwarding). |
![]() |
|
How did companies like Adobe and Microsoft become giants with a lot of money BEFORE subscription services? |
![]() |
| Local-first isn’t a good model for projects that need to monetize. I’ve given this a lot of thought as well. I think it only makes practical sense for apps that exist to serve a public benefit. |
![]() |
| "need to monetize" != Need to enshittify and squeeze every drop of blood from the stone.
There's a large spectrum of extreme profits before every getting to that point. |
![]() |
| So you're saying they've reached peak revenue? The best they can do is stabilize? And that's why we've reached a point that it wasn't working but now it is? |
![]() |
| Part of the ethos of local-first is permanent ownership of software over subscription services. Sure, it's not technically in the name, but it's very much part of the background for TFA. |
![]() |
| You can charge the user once upfront, possibly after a free trial. 37 signals are trying it out with once.com. As a developer, you are in control of how local-first your application should be. |
![]() |
| Them being not monetizable by you is exactly the reason why I should use them. You could charge me for a copy of the software, but that won't last forever like subscription cloud crapware does. |
![]() |
| A bit of an aside, but CRDTs are not always the best approach to solving the local-first distributed consistency problem. For the specific given example of syncing files it might make sense, but I'm starting to see CRDTs used in places they don't need to be.
Where is your ground truth? How collaborative is a given resource? How are merge conflicts (or any overlapping interactions) handled? Depending on your answers, CRDTs might be the wrong tool. Please don't forget about straightforward replicated state machines. They can be very easy to reason about and scale, although require bespoke implementations. A centralized server can validate and enforce business logic, solve merge conflicts, etc. Figma uses a centralized server because their ground truth may not be local.[1] If you try a decentralized state machine approach the implementation is undoubtedly going to be more complex and difficult to maintain. However, depending on your data and interaction patterns, they still might be the better choice over CRDTs. It could be argued that even for this example, two local-first clients editing the same file should not be automatically merged with a CRDT. One could make the case that the slower client should rename their file (fork it), merge any conflicts, or overwrite the file altogether. A centralized server could enforce these rules and further propagate state changes after resolution. [1] https://www.figma.com/blog/how-figmas-multiplayer-technology... |
![]() |
| Local first is the first software trend in a long time that has gotten me really excited. The aspect of co-located data and logic is what's most interesting for me for two reasons:
1. Easier to develop - Sync layer handles all the tricky stuff, no need for translation layer between server and client. 2. Better user experience - Immediate UI feedback, no network dependency etc. I suspect there will be a major tide shift within the next year or two when a local first framework with the developer experience similar to Nuxt or Next comes about. The Rails of local first. I can't recommend enough the localfirst.fm podcast which has been a great introduction to the people and projects in the space: https://www.localfirst.fm/ |
![]() |
| I've been a happy user of a PWA doing local sync. That said, the data it needs to sync can fit in localStorage.
Not affiliated in anyway, but the app is http://projectionlab.com/ and it allows you to choose between json import/export, localStorage sync, and server-based sync as desired. Since it has an easy to use import/export, sync with some other cloud provider on iOS is basically just a matter of "saving the file," since iOS lets you do background sync of authorized Files providers. Even though it's a web app, being able to download the page and then after run it entirely offline in a fresh browser window each time, built a lot of trust with me, to the point where I mostly run it with localStorage enabled and only occasionally check its online/offline behavior anymore. |
![]() |
| "syncing doesn’t work without a server"
I don't think this is true, granted there are some big challenges to transfering data between devices without a central server, but there are several projects like https://dxos.org/ which use p2p, and also there's https://ditto.live/ which uses bluetooth/wifi direct for cases where all users will be in the same room or on the same local network (imagine wanting to play chess with a friend sitting in a different row on a plane without wifi - I was in this situation recently and was pretty surprised that I couldn't find anything on the app store that could do this!) Of course most of the time its better to have a server because p2p still has a lot of difficulties and often having a central 'source of truth' is worth the costs that come with a server based architecture. So imo things like https://electric-sql.com/ or https://www.triplit.dev/ or the upcoming https://zerosync.dev/ will be far better choices for anyone wanting to build a local first app used by many users. |
![]() |
| I think that is, because KeepassXC has the logic to deal with a database file changing while having that file opened. Yes, can confirm, this works nicely. |
![]() |
| I’ve been pondering doing something like this with SQLite. The primary db is local/embedded on the user’s machine and use something like https://github.com/rqlite/rqlite to sync on the backend.
It also means it would be fairly trivial to allow users/orgs to host their own “backend” as well. |
![]() |
| Using theory of patches would better compliment the current approach. Integrating a scm such as https://pijul.org or atleast the underlying tech would allow for better conflict resolutions. Transferring patches should also allow for more efficient use of io.
|
![]() |
| glad to see more discussion about local-first, but there havn't been a good business model for local first products, which might lead to the unsustainable of the tech ecosystem |
![]() |
| (this should probably be a post)
CRDTs and local first are ideas that is perpetually in the hype cycle for the last decade or so, starting around the time Riak CRDTs became a thing and continuing all the way to today. Niki's post is a perfect illustration: CRDTs offer this "magical" experience that seems perfectly good until you try building a product with them. Then it becomes a nightmare of tradeoffs: - state-based or operation-based? do you trim state? how? - is it truly a CRDT (no conflicts possible), a structure with explicit conflict detection, or last-writer-wins/arbitrary choice in a trench coat that will always lose data? Case in point: Automerge uses pseudo-random conflict resolution, so Niki's solution will drop data if it's added without a direct causal link between the edits. To learn this, you have to go to "under the hood" section in Automerge docs and read about merge rules very attentively. It might be acceptable for a particular use case, but very few people would even read that far! - what is the worst case complexity? Case in point: yjs offers an interface that looks very much like JSON, but "arrays" are actually linked lists underneath, which makes it easy to accidentally become quadratic. - how do you surface conflicts and/or lost data to the user in the interface? What are the user expectations? - how do you add/remove replication nodes? What if they're offline at the time of removal? What if they come online after getting removed? - what's user experience like for nodes with spotty connection and relatively constrained resources, like mobile phones? Do they have to sync everything after coming online before being able to commit changes? - what's the authoritative node for side effects like email or push notifications? - how do you handle data migrations as software gets updated? What about two nodes having wildly different software versions? - how should search work on constrained devices, unless every device has the full copy of the entire state? Those tradeoffs infect the entire system, top to bottom, from basic data structures (a CRDT "array" can be many different things with different behaviour) to storage to auth to networking to UI. Because of that, they can't be abstracted away — or more precisely, they can be pretend-abstracted for marketing purposes, until the reality of the problem becomes apparent in production. From Muse [1] to Linear [2], everyone eventually hits the problems above and has to either abandon features (no need to have an authoritative email log if there are no email notifications), subset data and gradually move from local first to very elaborate caching, or introduce federation of some sort that gravitates towards centralisation anyway (very few people want to run their own persistent nodes). I think this complexity, essential for local-first in practice, is important to contextualise both Niki's post and the original talk (which mostly brushed over it). [1]: https://museapp.com/podcast/78-local-first-one-year-later/ |
![]() |
| There is another important and too often ignored situation: sw availability.
Let's say a day BeanCount (my preferred personal finance software) disappear, well, so far I can switch to Ledger or HLegder, the switch demand a bit of rg/sed works but it's doable. If let's say Firefly disappear I still have my data, but migrating them to something else it's a nightmare. Of course such events are slow, if the upstream suddenly disappear the local software still work, but after some times it will break due to environmental changes around it. With classic FLOSS tools that's a limited problem, tools are simple without much dependencies and they are normally developed by a large spread community. Modern tools tend to be the opposite: with gazillion of deps often in https://xkcd.com/2347/ mode. My digital life is almost entirely in Emacs, the chances Emacs disappear are objectively low and even if it happen even if it have a very big codebase there are not much easy-to-break deps BUT if I decide to go the modern path, let's say instead of org-attaching most of my files I decide to put them on Paperless and use them instead of via org-mode notes links with Dokuwiki or something else I get much more chances something break and even if I own anything my workflow cease to exists quickly. Recover would be VERY hard. Yes, paperless in the end store files on a file system, I can browse them manually, Zim have essentially the same Dokuwiki markup so I can import the Wiki, but all links will be broken and there is no direct quick-text-tweaking I can apply to reconstruct http links to the filesystem. With org-attach I can, even if it use a cache-like tree not really human readable. Anyway to have personal guarantees of ownership of our digital life local-first and sync are not the only main points. The corollary is that we need the old desktop model "an OS like a single program, indefinitively extensible" to be safe, because it's much more fragile at small potatoes level, but it's much more resilient in the long run. |
![]() |
| Specifically because investors cared more about MRR and ARR than any specific numbers - this conversion is really what turned everything into a SaaS company. |
![]() |
| Fully agree regarding the motivations. The thing many young devs don't realize that there's a reason for doing things a certain way. It's not "just because this is the best way to build everything." |
![]() |
| As for the next cycle, the internet computer protocol is an interesting punt - a distributed network of servers, with replication and a degree of anonymity, where people host and pay for compute |
![]() |
| It's a personal blog made for fun. Both features made me smile. What's the problem? The kind of attitude you're displaying here just depresses me. Makes me lose faith in humanity. |
![]() |
| At the very least it should be toggleable. It's distracting and impedes readability.
Ostensibly, the goal of writing a blog post is for others to read it. |
![]() |
| I think is the goofiest and most lovely feature. It gave me a weird sense of community presence while reading (and most people keep their cursors off the text anyway) |
![]() |
| I really dislike this. Sure...it's fun, but where did I "sign up" to allow others to see where my mouse pointer is? How do I disable it? What else, from my machine, is being captured and broadcast? |
![]() |
| Moving pointers mean most people (including me) will never read the actual content, so we might as well discuss why someone would choose to alienate their potential audience in this manner. |
![]() |
| Some people also just want to have fun on the internet, remember Myspace, Tumblr etc? These sites were hardly functional, inclusive or accessible but they sure were fun. |
For example, we're building a local-first multiplayer "IDE for tasks and notes" [1] where simply syncing flat files won't work well for certain features we want to offer like real-time collaboration, permission controls and so on.
In our case we'll simply allow users to "eject" at any time by saving their "workspace.zip" (which contains all state serialized into flat files) and downloading a "server.exe/.bin" and switch to self-hosting the backend if they want (or vice versa).
[1] https://thymer.com/