ADR 0009 — A hand-rolled WebSocket transport
- Status: Superseded by 0008
- Date: 2025-05 (decided), 2026-05-20 (reverted)
Context
stitchkit originally planned to own its real-time layer: a WebSocket transport
built directly on Bun.serve()’s native WebSocket, with no Socket.IO.
The rationale at the time:
- Zero dependencies — consistent with ADR 0001.
- Bun’s native WebSocket (uWebSockets underneath) is faster than Socket.IO.
- Socket.IO’s main job is a polling fallback, assumed unnecessary in 2026.
- Rooms are about 30 lines to implement.
Decision (original)
Build a native WebSocket stack: defineEvents for a typed event registry,
createWebSocketHandlers for the server, createSocketClient for the client,
an entity emitter, a useSocketEvent React hook, and cookie-based handshake
auth via an onAuth callback.
Why it was reverted
Every project consuming stitchkit already runs on Socket.IO — for the polling fallback, the heartbeat, acknowledgements and a mature reconnection client that a hand-rolled transport would have to re-earn. The native stack was never wired into a single consumer. It was about 700 lines of dead code.
ADR 0008 replaced it with thin Socket.IO wrappers (createSocketIOClient /
createSocketIOServer). The entire native stack was deleted.
Consequences
- A real transport was built, shipped, and never adopted — a cost paid in full.
- The lesson, recorded in ADR 0008: do not build a transport the consumers will not use. Wrap the one they already run on.