Thinking in operations
If you've built CRUD apps before, you probably think in terms of rows — the current value of a record in a database. asks you to flip this: think in terms of the sequence of changes that produced that row.
This lesson is short on code and heavy on mental model. Once it clicks, the rest of the stack falls into place.
The snapshot trap
Here's a SQL-shaped way to handle "mark todo done":
UPDATE todos SET done = true WHERE id = 't-1';
That statement destroys information. Afterwards, you can't answer:
- When did it get marked done?
- Who marked it?
- What was it before?
- Can we undo just this change without rolling back everything since?
You can rebuild some of that with audit tables, triggers, or soft deletes — but you're reintroducing history on top of a model that actively throws it away.
The operation-shaped answer
In Powerhouse, the same change is:
dispatch(completeTodo({ id: "t-1" }));
Which becomes an operation appended to the document's log:
{
"index": 42,
"timestamp": "2026-04-15T10:12:33Z",
"actor": "user:alice",
"name": "completeTodo",
"input": { "id": "t-1" },
"hash": "…"
}
State is derived. If you want "the current list of done todos," you fold the log. If you want "the state at index 30," you fold up to there. If two peers edit offline, you merge their logs in order — no last-write-wins guesswork.
What you get for free
Once the log exists, a lot of hard problems become easy:
- Undo / redo — pop (or invert) the last operation.
- — fold to any index to see the document as it was.
- Audit trails — every change has an actor, a timestamp, and a hash.
- Derived read models — a processor subscribes to the stream and maintains a search index, or a counter, or a cache.
- Replays — reconstruct bugs by replaying the exact operations the user produced.