mlajtos.mu
RSS/Milan LajtošGitHub/Milan LajtošYouTube/Milan LajtošBuyMeACoffee/Milan Lajtoš

Serverless Collaborative Hierarchical TODO App in 200 LoC

Few days ago, I discovered Valtio – a state management library with following tagline:

Valtio makes proxy-state simple for React and Vanilla

And it really is simple – you treat your state as plain old JS objects, you mutate them however you like, and it all just works. But wait, there is more. Much more!

Compounding Ideas

For past few months I have been researching CRDTs a lot. CRDT stands for Conflict-free Replicated Datatype, and you can imagine it as any data structure (string, array, map, etc.), but it is replicated, i.e. there are multiple copies – I have a copy, you have a copy, and Joe has a copy. All our copies are mutable – any of us can change it at any time – so we have to somehow synchronize them. We can have a 3rd party arbiter that would do it for us, but ideally we don't want to introduce any central authority. If there isn't an arbiter that would resolve conflicts for us, we have to do it in some distributed way. And this is why CRDTs exist.

CRDTs are relatively new tech, but they are gaining traction in the business – non-exhaustive list of CRDT implementations. Since we are in the JS context, the most interesting implementation for us is Yjs. And surprise, surprise – Valtio has Yjs support.

TodoMVC – A Webapp Benchmark

TodoMVC can be viewed as Rosetta Code for web applications. One app – managing list of TODO tasks – same functionality written in dozen frameworks. The problem is easy, so you can direct your attention to understanding the tech. It is also an awesome litmus test to determine how awful/wonderful a framework can be.

In the spirit of TodoMVC I wanted to try if Valtio and Yjs are worthy. But I wanted a harder challenge. I like to break my TODOs into a tree, where high-level tasks are subdivided into smaller & actionable chunks. This method helps me to keep context while doing necessary small steps towards my goal. On the other hand, I was aiming for the visual minimalism & crude UX, so don't expect a polished product, rather a good starting point. So, without further ado, I present to you...

Source Code | Demo

Beauty, isn't it? Go ahead and open it in multiple browsers, send the link to your friend and see for yourself if it really works.

Standing on the Shoulders of Giants

If you open the source code and start reading and counting, you'll may be a little surprised. In those 200 lines of code, there are all the filespackage.json (31), tsconfig.json (14), index.html (18) and finally index.tsx (136), which gives you the total of 199. The code is fully typed and there are no compression techniques involved. It's mostly super-boring code.

Of course, import universe is just a single line, so what does all dependencies weight? A pretty hefty 363kB of minified code on the frontend. That is not a small number, but all that pile enables you to write collaborative applications with dead-simple code.

I should also mention the "serverless" part, which might be controversial. Of course, with all "serverless" there is a server that you don't own. This applies for this example as well. However, the server has only transport role – relaying messages from one client to others. If you want more peer-to-peer topology, you can switch to different Yjs provider, e.g. WebRTC, DAT, or GossipSub. If you feel crazy enough, you can write your own provider on top of some blockchain...

Please don't.

The Other 10.000-hour Rule

Web can & should be more collaborative than it currently is. Sometimes, I ask myself: "If I had 10.000 engineers for an hour of their precious time, how could I utilize them to do something truly meaningful?" Our current tools for collaboration are useless for this kind of question. I hope that in the future we will be able to do this, but sadly we are not there yet. Maybe for some tasks this is already doable – after all, r/place was a beautiful social experiment, but it wasn't useful in any other way. What can be done in a single hyper-focused hour with so many people? What are the tools that enable us to embed them in a meaningful collaborative way? How can we divide & conquer such tasks?

share state;
push pixels;
have fun

Notes:

  • As per request from Daishi Kato, the author of Valtio, here are my thoughts about the lib: Data is a Bad Idea – in the spirit of Alan Kay, sharing only dead data is not enough. We should be able to share code which can interpret the data as well. Simply put, proxy({identity:x=>x}) should be shareable by textual or AST serialization out of the box. Of course, there can't be any guarantees whether the receiver can/should/will run the code, but it seems like a step in a...funny direction.