Explanation
Design Choices and Tradeoffs
Understand the design decisions, alternatives, and limits.
- Shared undo/redo belongs near the authoritative state, not in each browser tab.
- Preview operations should use the same data shape as durable operations but must not increment revision.
- Redis is a coordination layer, not a canvas-state cache; that keeps failure recovery tied to PostgreSQL snapshots and revisions.
- Revision-gap recovery is cheaper than making every realtime event durable because cursor, preview, and presence messages are intentionally ephemeral.
- Fixed-window rate limits are simple and predictable; the tradeoff is bucket-edge burstiness, which is acceptable for this collaboration workload.
- Folders should organize owner-owned canvases only; sharing is a separate access graph.
- Selection reconciliation must run after local, remote, undo, and redo operations so grouped children cannot remain individually editable.
- SVG is a practical editor substrate when geometry helpers, rendered bounds, and viewport math are kept explicit.
- Access removal must affect live sockets, not just future HTTP requests.
- Whole-object text styling keeps collaboration and undo semantics tractable; per-span rich text would require a deeper text operation model.
sticky sessions only -> rejected: correctness should not depend on load-balancer affinity PostgreSQL LISTEN/NOTIFY for everything -> rejected: high-frequency preview/cursor traffic should not live in the durable database path Redis Streams for all collaboration events -> rejected: durable operations are already recoverable by revision snapshot Redis canvas-state cache -> rejected: adds invalidation around the most important state
The alternatives mostly fail by putting the wrong kind of state in the wrong layer. Sticky sessions make the load balancer part of the correctness story. PostgreSQL notifications make transient cursor traffic compete with durable database work. Redis Streams add replay machinery for events that either do not need replay or can already be recovered from PostgreSQL. A Redis canvas cache would speed up some reads but complicate the only state that must never be ambiguous.
The project is less about one isolated trick than about making many small contracts agree: JSON shape state, relational ownership, WebSocket rooms, Redis coordination, React interaction state, server-side history, and a serious dashboard UI. LiveBoard works because each boundary is narrow enough to explain and direct enough to test, while the complete product still feels like one continuous collaborative surface.