> Ever-growing state: for CRDTs to work well they need to keep a record of both what exists, and what has been deleted (so that the deletes aren’t accidentally added back in later). This means that CRDT state will continually expand. There’s a bunch of magic that CRDT library authors are doing with clever compression techniques to make this problem less-bad, but it’s basically in-escapable. The size of your CRDT state is not purely a function of the size of the state the CRDT represents, but also of the number of updates that state has gone through.

A) This is only the case for certain CRDTs, such as sets that support deletion - so, if you want Set semantics with deletion support, you need two sets, one to that tracks all deletions and one that tracks all insertions.

B) You can garbage collection your sets. They don't have to grow forever.

> Complex implementations: CRDTs are easy to implement wrong, so probably don’t roll your own.

Personally, I've never done this. I've just added a `merge(&mut self, other: &Self)` method to structs in Rust. Guaranteeing CRDT properties is often trivial, or at least it was in my case.

> Opaque state: Because the CRDT has to represent both the underlying state and the updates that led to that state

Again, this is only if you need specific operations on your CRDTs and if your CRDTs are encoded in specific ways.

I've said it before, but a trivial crdt looks like this

struct Grows(u64);

impl Grows {
fn merge(&mut self, other: &Self) {
self.0 = max(self.0, other.0);
}
}

et voila? Obviously you lose all intermediary states, but since that is specified to be a negative thing, I just want to be clear that it's often optional.

> So maybe you are convinced that CRDTs are not the be-all-and-end-all of collaboration, and that you aren’t in one of the two categories where you probably should use a CRDT, and you’ve made it this far in the post.

I am convinced that CRDTs are not the be-all-and-end-all, because Strong Eventual Consistency does not provide strong enough guarantees for all use cases.

Once again we have a CRDT article that's about user collaboration, which I find somewhat frustrating because CRDTs can be used in far more places than that, and user collaboration is like the most complicated thing you could ever write since it's all of the problems of a distributed system and then we add humans into the mix. There is no "good" solution to this problem - CRDTs aren't going to solve it, and neither is any other algorithm, because it's not possible to encode every possible state update in a way that never conflicts and is also what a human expects (especially since humans have varying expectations).

The algorithm/ approach, as described, seems perfectly fine - it will have edge cases just like CRDTs will. In reality, for such an impossibly complex problem, you're probably going to end up with something really complex to solve it. You're almost certainly going to start adding CRDT-like operations, like "ok technically this user held a lock on X, but the other user performed an operation on X that technically commutes, so we can allow both" to alleviate some of the inherent complexities (and UX issues) with locking.