Build a Crash-Safe Collaborative Editor with Journaling
Computer Science
Build a Crash-Safe Collaborative Editor with Journaling
Build a Crash-Safe Collaborative Editor with Journaling
Build a small browser editor that records incremental changes in a durable journal, periodically saves full checkpoints, and restores unsaved work after a simulated crash. Along the way, you will make the tradeoff between frequent journal writes and larger checkpoint writes visible in a runnable project.
What you learn by building this
- Represent editor changes as ordered, incremental operations
- Persist journal entries and full checkpoints using a simple local storage layer
- Recover the latest editor state by replaying journal entries after a checkpoint
- Simulate a crash and verify that unsaved changes are restored
- Explain why incremental writes complement rather than replace full checkpoints
- Expose recovery behavior through a small browser-based editor
Learning Journey
Make the Editor Work Before Persistence
3 lessonsCreate the smallest usable editor and establish the document-change model that later lessons will persist. This module stands alone so the learner can run and demonstrate the project immediately.
Add Journaling and Full Checkpoints
3 lessonsIntroduce the two complementary persistence paths: frequent incremental journal entries and less frequent full checkpoints. The learner will make sequence numbers and recovery metadata observable before simulating failure.
Recover After a Simulated Crash
4 lessonsUse the project’s checkpoint and journal data to reconstruct the latest document state after in-memory state disappears. The finished project will provide a repeatable crash-and-recovery demonstration.
Public lesson
Create a Runnable Browser Editor
Predict
What will happen?
Make the browser editor respond to editing
The editor will be more useful if it reports what is happening as you type. Before changing anything, predict what you expect:
If the document starts with some text and you type three characters, should the status panel update only after a refresh, or immediately?
The browser can observe each edit through the input event. The Node.js server’s job is to deliver the page; after the page loads, the browser handles the interaction.
Tasks
1. Add the document area and status panel
Open the existing server entry file and find the HTML that it sends for the main page. Inside its <body>, make the document and status elements explicit:
<main>
<h1>Browser Editor</h1>
<div
id="document"
contenteditable="true"
role="textbox"
aria-label="Document"
>
Start writing here.
</div>
<p id="status" aria-live="polite">TODO: initial status</p>
</main>
<main>
<h1>Browser Editor</h1>
<div
id="document"
contenteditable="true"
role="textbox"
aria-label="Document"
>
Start writing here.
</div>
<p id="status" aria-live="polite">TODO: initial status</p>
</main>
Adapt the surrounding markup rather than replacing unrelated code. The important relationship is:
#documentis where the learner edits.#statusis where the page reports the current document state.contenteditable="true"makes an ordinary element editable without adding a framework.
Run the project using its documented command and open the local address it prints. Click the document area and type. At this point, the text should be editable, but the status will not react yet. That failed check is useful: it shows that making something editable and observing it are separate jobs.
Tasks
2. Connect edits to the status panel
Add a script near the end of the page, after the two elements above:
<script>
const documentArea = document.querySelector("#document");
const status = document.querySelector("#status");
function updateStatus() {
// Choose the text property that should represent the visible document.
// Then replace the placeholder status message.
}
// Register the event that fires when the user changes the document.
// Call updateStatus from the event handler.
updateStatus();
</script>
<script>
const documentArea = document.querySelector("#document");
const status = document.querySelector("#status");
function updateStatus() {
// Choose the text property that should represent the visible document.
// Then replace the placeholder status message.
}
// Register the event that fires when the user changes the document.
// Call updateStatus from the event handler.
updateStatus();
</script>
Complete the two comments yourself. Your status should show the number of characters in the document, ignoring leading and trailing whitespace. One expression that helps is:
documentArea.innerText.trim().length
documentArea.innerText.trim().length
For example, the body of updateStatus could be shaped like this, but adapt the wording to your editor:
const characterCount = /* calculate the trimmed character count */;
status.textContent = /* make a readable status message */;
const characterCount = /* calculate the trimmed character count */;
status.textContent = /* make a readable status message */;
The event listener should listen for "input":
documentArea.addEventListener("input", () => {
// update the status here
});
documentArea.addEventListener("input", () => {
// update the status here
});
The separate updateStatus() call matters because the document already contains initial text when the page loads. Without it, the status would remain wrong until the first keystroke.
Tasks
3. Check the behavior in the browser
Reload the page, then check these cases:
- The status is correct before you type.
- Add one character. The status changes immediately.
- Select all the document text and replace it. The status changes again.
- Delete all the text or enter only spaces. The trimmed count becomes zero.
- Refresh the page. The initial document and its initial status return.
Find the bug
Something's wrong — can you spot it?
If the text changes but the status does not, inspect the browser’s developer console. The most useful questions are:
- Does
querySelector("#document")find the same element whoseidyou wrote? - Does the status element really have
id="status"? - Is the script running after those elements exist?
- Did the server send the newest version, or does the browser need a hard refresh?
What this small change demonstrates
There are two different moments in this app:
Node.js server
└── sends the HTML page
└── browser loads it
├── user edits #document
└── input event updates #status
Node.js server
└── sends the HTML page
└── browser loads it
├── user edits #document
└── input event updates #status
The server does not need to handle every keystroke for this first editor. It only serves the page. The browser owns the immediate interaction, so the feedback feels instant and the server remains simple.
This distinction will matter if the editor later needs saved documents or multiple users: local editing can stay responsive, while a separate server feature can decide when and how changes are stored or shared. For now, the visible check is deliberately modest: edit text in the browser and watch the status panel prove that the page noticed.
Course Outline
3 modules · 10 lessons
Make the Editor Work Before Persistence
Add Journaling and Full Checkpoints
Recover After a Simulated Crash
Learn by building your own version.
Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.