Work/Crypto Exchange · Frontend Platform

RcoinX — Real-time Spot Trading

A pre-launch cryptocurrency exchange needed a frontend platform that could render live market data for thousands of concurrent users without dropping frames — starting from an empty repository.

Role
Senior Frontend Engineer
Timeline
9 months
Team
3 engineers · I authored 61% of commits
Year
2025–2026
Search SOL, open it, switch the chart to 15m, then a market order for 0.25. The book and tape keep moving underneath.
~9.5K
LOC of Spot Trading, end-to-end
3
WebSocket streams multiplexed
8
shared packages in the monorepo
61%
of team commits authored

01 — Problem

An exchange frontend, from an empty repo

RcoinX was pre-launch: no design system, no repository structure, no data layer — but a hard requirement that Spot Trading feel instant. An order book updates many times per second; a naive render pipeline melts at that rate, and a naive architecture melts the team once three engineers work in the same codebase.

The real problem was two problems: make real-time market data cheap to render, and make the codebase safe to grow. I owned both.

02 — Constraints

What made it hard

Everything had to be built while the backend was still moving — API contracts changed weekly. The UI had to support RTL and LTR from day one for a bilingual market. And with a 3-engineer team, any architecture that required constant coordination would have been slower than no architecture at all.

  • Market data arrives on 3 concurrent WebSocket streams, at up to tens of messages per second per symbol.
  • React re-renders are the enemy: an order book that re-renders on every tick is unusable.
  • Multiple screens need the same live data — subscriptions must be shared, not duplicated.
  • Backend contracts unstable → the frontend needed its own typed boundary and mock layer to keep shipping.

03 — Architecture

A ref-counted WebSocket layer

The core decision: no component talks to a socket. A centralized WebSocket layer owns connections and exposes subscribe(topic) — the first subscriber to a topic opens the upstream subscription, the ref-count tracks consumers, and the last unsubscribe tears it down. Components mount and unmount freely; the socket layer decides what actually flows over the wire.

Incoming ticks never touch React state directly. They patch a normalized cache, throttled to a 200–300ms cadence — fast enough to feel live, slow enough that the order book renders at a fixed, predictable rate. Auto-reconnect with resubscription replays the ref-count table after a drop, so a network blip never leaves a stale screen.

Diagram of the ref-counted WebSocket layer: streams multiplexed into a throttled cache feeding UI slices
Three channels share one socket. The first subscriber opens the stream, the last unsubscribe closes it, and ticks land in a cache at 200–300ms.

04 — Platform

A monorepo the team couldn't break

I structured the codebase as a Turborepo monorepo with 8 shared packages (ui, providers, http, i18n, types, cross-feature APIs) and strict Feature-Sliced Design import boundaries enforced by ESLint. A feature can't reach into another feature's internals; a page can't import a raw API client. The linter, not code review, holds the architecture.

RTL/LTR Storybook components and Vitest + MSW contract tests run in Docker CI, so backend churn broke tests, not production.

05 — Security

The dialog opens after the session exists

Sensitive account actions never open a form first. Linking an authenticator, adding a passkey, or changing an email creates an MFA session, and the dialog mounts only after that session exists. The stepper will not move on until the code — email, TOTP, or WebAuthn — actually verifies.

Which methods exist is a fact about the user, not a guess inside the component. Email, authenticator, and passkey are flags on the profile. Phone has to be set before passkey or TOTP management unlocks, so the security page cannot offer a method the account is not ready for.

RcoinX security settings with email, passkey, and TOTP two-factor authentication enabled
Account security: email, passkey, and TOTP, each marked set only when the profile actually has that method.

06 — In use

The same gate, a different title

The security page is where a method gets turned on. Everywhere else, the gate is one dialog. Password, email, phone, authenticator, and passkey each create a session first, then walk an SMS code, then a choice of email, passkey, or TOTP. The step after that — a new password, a QR code — only appears once that choice verifies.

What changes is the title. The steps do not.

After SMS, the gate offers email, passkey, or TOTP. The video continues through the authenticator code and the new password, and stays open until Done.

07 — Outcome

What shipped

Spot Trading shipped end-to-end (~9.5K LOC): order book, candlestick charts, order management, 9 REST integrations, 3 WebSocket streams. Three engineers shipped in parallel without stepping on each other, and the WebSocket layer hasn't needed structural changes since it landed.

Order book and depth, updated from the throttled cache. Ten seconds, silent, and safe to loop.

Stack

React 19TypeScriptTanStack RouterTanStack QueryZustandWebSocketTurborepoTailwind CSSRadix UIStorybookVitestMSW

Next case

Chatomatic — AI Assistant SaaS→