System design
How this site
actually works.
Not the brief it was commissioned from — the system that exists. The request path, the data model, the twenty phases it was built in, and the trade-offs behind the parts that are deliberately not here.
The request path.
Five layers, and one rule that shapes all of them: a Vercel function keeps nothing in memory between requests, so every piece of state lives in Postgres or in the URL.
The data model.
Nine of the principal tables. The through-line is referralRef: one column on three unrelated conversion paths is what turns short links into attribution without touching any of them.
day column on purpose — Prisma has no date truncation in groupBy, so materialising the bucket key keeps a daily chart proportional to the number of days rather than to traffic.Built in twenty phases.
Each one shipped and verified before the next started, and all twenty are deployed and reachable today. Five are marked as waiting on something outside the code — four on a credential, one on a DNS move. Those phases are live; one feature inside each stays switched off, degrading quietly rather than erroring, until it lands.
- P1FoundationTS, Tailwind, CIlive
- P2AuthAuth.js v5, RBACneeds OAuth apps
- P3AdminPrisma CRUD, gatedlive
- P4Contentranked search, sitemaplive
- P5IntegrationsGitHub, LeetCodeneeds Cal.com
- P6CommsSSE chat, web pushneeds a domain move
- P7PaymentsRazorpay, invoicesneeds Razorpay keys
- P8Growthshort links, QRlive
- P9StorageR2, content-addressedneeds R2 keys
- P10Analyticsfirst-party, cookielesslive
- P11Realtimepresence, live dashboardlive
- P12Deliveryvendored fonts, split CSSlive
- P13SecurityCSP, CSRF, HSTSlive
- P14DevOpshealth checks, metricslive
- P15QA41 E2E, axelive
- P16AIhybrid retrieval, citedlive
- P17DevEx UICmd+K, terminallive
- P18DistSysoutbox, leases, Raftlive
- P19Low-levelWASM, threads, WebGPUlive
- P20Data engELT, DuckDB, lineagelive
Decisions worth defending.
The interesting part of a system is usually what it does not do. Open any one of these to read the argument.
SSE and a DB poll, not WebSockets
Vercel functions cannot hold a socket open, and module-scope memory is not shared between invocations. Chat streams over SSE; typing indicators are deadline columns in Postgres rather than in-process state.
Redis is optional, not infrastructure
The brief made it load-bearing. The limiter prefers Upstash when a key is configured and falls back to an in-memory token bucket per instance when one is not — a weaker ceiling, accepted deliberately, because a limiter that returns 429 while its own dependency is down has converted someone else's outage into ours. Anything that must actually hold, like payment capture, is guarded by auth and idempotency keys instead.
The answer is extractive by default
Retrieval is hybrid: dense vectors and Postgres full-text search fail in different ways, so both run on every question and the results fuse by reciprocal rank. The answer is then assembled from sentences that exist verbatim on the site, each linking to where it came from. Generation is opt-in and runs in the visitor's own browser — on a page about a real person's real work, a fluent invented claim is not a smaller failure than an awkward true one.
Reads degrade instead of failing
Every public read goes through readOrFallback. A database blip renders an empty section rather than a 500, which is also why the build survives having no database at all — the CI job asserts exactly that.
Heatmaps are aggregates, never raw points
An exact click trail is a behavioural fingerprint and grows without bound. Counts live per grid cell, created lazily, so the table tracks where people click rather than how much traffic there was.
Charts are hand-written SVG
Including the two on this page. They are server-rendered from data already in hand, so no charting library ships and nothing here hydrates.
Money is integer minor units
End to end, never floats. Bookings only reach CONFIRMED through a signature-verified webhook, never from a client callback.
Spec versus reality.
The original brief asked for a larger stack than this. Each row is what it specified, what runs instead, and why — including the four that have shifted since this table was written, two of them from not built to built.
| Specified | Built | Why |
|---|---|---|
| Redis | Upstash-capable, memory today | no key set; it degrades rather than failing closed |
| WebSockets | SSE + DB poll | serverless cannot host a socket |
| pgvector / RAG | built in P16 — hybrid, cited | the embedding is in-process TF-IDF; every hosted one is metered |
| Docker | a Dockerfile, not the deploy path | it proves nothing here depends on Vercel |
| Better Auth | Auth.js v5 | the spec allowed the swap |
| Stripe | Razorpay | India-side rails |
| Playwright / Lighthouse CI | 41 E2E + axe + Lighthouse | built in P15; green in CI since run #38 |
Consensus, watchable.
Raft leader election, running live. The rules are a pure state machine in lib/distsys/raft.ts with nineteen tests asserting what the protocol actually guarantees — a term only increases, two leaders cannot exist in one term, a leader that loses quorum steps down. This component only draws it and drives the clock.
Kill the leader and watch a new one get elected. Kill a third node and watch it correctly elect nobody: two of five cannot form a majority, and stopping is the right answer rather than splitting the cluster in half.
5/5 up · majority needs 3 · electing…
- t0Cluster of 5 started. All followers, term 0.
The code is public if you would rather read it than take my word for any of this.
