[US-18] As a user, I want to choose between live (real-time, autosaving) and transactional (explicit save) editing modes where the individual app supports it, so I can control when my changes become visible to collaborators. #8

Open
opened 2026-08-21 17:46:52 +02:00 by angie · 3 comments
Owner

@yousef For me, this is not a not a must-have.

I'm concerned it comes with significant engineering and UX challenges. The main one being; what happens to local unsaved changes when the user accidentally closes the browser / computer?

@yousef For me, this is not a not a must-have. I'm concerned it comes with significant engineering and UX challenges. The main one being; what happens to local unsaved changes when the user accidentally closes the browser / computer?
Author
Owner

@jeremiah Personally, I want things to always be saved. Hovering over command+S is a relic.

If I want a checkpoint in the edit history, I save a specific revision. In Google Docs, File → Version history → Name current version does this.

@jeremiah Personally, I want things to always be saved. Hovering over command+S is a relic. If I want a checkpoint in the edit history, I save a specific revision. In Google Docs, File → Version history → Name current version does this.
angie changed title from [US-18] to [US-18] As a user, I want to choose between live (real-time, autosaving) and transactional (explicit save) editing modes where the individual app supports it, so I can control when my changes become visible to collaborators. 2026-08-21 18:06:45 +02:00
Owner

in the NG prototype, we already implemented this mechanism. A document can be opened in "live editing" (real-time) or not.
There is no overhead in engineering as it is done at the CRDT level. All CRDTs are compatible with that. What we do is that we store the updates locally. They are persisted locally so a refresh wouldn't lose them. But they are just not sent yet to the other replicas. They accumulate locally, and when the user wnats to "Send" or "save", they just press the save button (cmd+s). In terms of UX, it is understandable what save will do. We can even add a "cancel" button.

I agree that it is a "relic". But in local-first settings, the topic is coming back. Google docs and other collaborative tools brought the "always sync, always collaborate" concept, and there is no need to press save. We do support that, obviously, and it could be the default mode (it isn't for now in the NG prototype).

The rational behind bringing back this "save feature", is a bit distinct from the "save a revision" one that Jeremiah mentions. The save a revision feature can only mark in time, something that was collaboratively edited. But it does not allow me to "prepare some changes locally and only share them with others when I am ready". Sometimes you don't want each character to be seen by the others, as they are typed in. Sometimes you want to be free to draft a sentence, change it, and only send it once you are happy with it. If the document is collaborative and there is no way to temporarly deactivate the collaborate, then you can't do that, and you have to open a notepad on the side to draft your sentence and then past it into the shared doc.

Finally, this feature is technically important, for 3 reasons :

  • debouncing : as saving each character one by one in a separate commit is very costly, every editor should instead debounce keystrokes and send few characters all at once. debouncing can be done with a timer, after inactivity (if user stops writing for 1 sec, we send what we buffered). and also with a upper limit of few seconds of continuous typing after which we do send what we have anyway. As we do not want each editor/app to implement that themselves, we do it at the CRDT level so every collaborative app can benefit from debouncing.
  • as we have the feature of "packing commits together" anyway, we can then also offer it as "draft" feature. This relates a bit with the fork/merge feature of NG, but it is different. the fork/merge is similar to git. You fork, then you can work on the branch separately, and once you are done, you can merge back. the branch is editable collaboratively with other devices and users. But the draft feature is simpler. The local changes do not leave the device/replica. you don't see them in other tabs. It is lightweight. Because fork and branches is kind of heavy in terms of metadata. The "draft" mode is just saving the CRDT patches locally until you press save.
  • when the device is offline, we do internally switch to this "draf" mode. There is no need to encode and encrypt each keystroke one by one, as anyway we are offline. And when the device is back online, we "save" and send what was locally changed (unless the draft mode is activated, and in this case we wait until the user actually presses the save button).
in the NG prototype, we already implemented this mechanism. A document can be opened in "live editing" (real-time) or not. There is no overhead in engineering as it is done at the CRDT level. All CRDTs are compatible with that. What we do is that we store the updates locally. They are persisted locally so a refresh wouldn't lose them. But they are just not sent yet to the other replicas. They accumulate locally, and when the user wnats to "Send" or "save", they just press the save button (cmd+s). In terms of UX, it is understandable what save will do. We can even add a "cancel" button. I agree that it is a "relic". But in local-first settings, the topic is coming back. Google docs and other collaborative tools brought the "always sync, always collaborate" concept, and there is no need to press save. We do support that, obviously, and it could be the default mode (it isn't for now in the NG prototype). The rational behind bringing back this "save feature", is a bit distinct from the "save a revision" one that Jeremiah mentions. The save a revision feature can only mark in time, something that was collaboratively edited. But it does not allow me to "prepare some changes locally and only share them with others when I am ready". Sometimes you don't want each character to be seen by the others, as they are typed in. Sometimes you want to be free to draft a sentence, change it, and only send it once you are happy with it. If the document is collaborative and there is no way to temporarly deactivate the collaborate, then you can't do that, and you have to open a notepad on the side to draft your sentence and then past it into the shared doc. Finally, this feature is technically important, for 3 reasons : - debouncing : as saving each character one by one in a separate commit is very costly, every editor should instead debounce keystrokes and send few characters all at once. debouncing can be done with a timer, after inactivity (if user stops writing for 1 sec, we send what we buffered). and also with a upper limit of few seconds of continuous typing after which we do send what we have anyway. As we do not want each editor/app to implement that themselves, we do it at the CRDT level so every collaborative app can benefit from debouncing. - as we have the feature of "packing commits together" anyway, we can then also offer it as "draft" feature. This relates a bit with the fork/merge feature of NG, but it is different. the fork/merge is similar to git. You fork, then you can work on the branch separately, and once you are done, you can merge back. the branch is editable collaboratively with other devices and users. But the draft feature is simpler. The local changes do not leave the device/replica. you don't see them in other tabs. It is lightweight. Because fork and branches is kind of heavy in terms of metadata. The "draft" mode is just saving the CRDT patches locally until you press save. - when the device is offline, we do internally switch to this "draf" mode. There is no need to encode and encrypt each keystroke one by one, as anyway we are offline. And when the device is back online, we "save" and send what was locally changed (unless the draft mode is activated, and in this case we wait until the user actually presses the save button).
Owner

I think we need to keep in mind that the fact that we can do something, doesn't mean we necessarily should, especially not in a first version.

It's good to make sure we can architecturally support these kind of scenarios, but I wouldn't underestimate the work to surface it to end-users. For example, the upwelling paper shows there's tons of ways to do this, and how different patterns might cater to different users. Saving real time (when possible) imo is a sensible default, and we could always add features like these for advanced authoring later.

I think we need to keep in mind that the fact that we _can_ do something, doesn't mean we necessarily _should_, especially not in a first version. It's good to make sure we can architecturally support these kind of scenarios, but I wouldn't underestimate the work to surface it to end-users. For example, the [upwelling](https://www.inkandswitch.com/upwelling/) paper shows there's tons of ways to do this, and how different patterns might cater to different users. Saving real time (when possible) imo is a sensible default, and we could always add features like these for advanced authoring later.
Sign in to join this conversation.
No milestone
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ELFA/rfd#8
No description provided.