← Ilona Golman · Blog
Field notes · System Design
▶ source · Hayk Simonyan · 2h04m
From foundations to production infra

How real systems are built —
one component at a time

A clickable walkthrough of the full system-design course by Hayk Simonyan: APIs, databases, scaling, load balancing, auth and security. Start with one server, end with infrastructure that survives millions of users.

📺 Hayk Simonyan 2h 04m · 16 lessons 👁 262K views 🎯 for the system-design interview round
01

Foundations

Servers, databases, scaling, load balancing, points of failure

02

API Design

Contracts, protocols, REST & GraphQL, versioning

03

Data & Storage

SQL vs NoSQL, consistency, choosing the right store

04

Scale & Reliability

Caching, performance, redundancy, failure handling

05

Auth & Security

Authn vs authz, tokens, RBAC, HTTPS, CORS

Foundations · 01

Everything starts on one server

Designing for millions of users is hard — so you never start there. You start with a setup that serves a single user, understand every piece, then add complexity only when something forces you to. In the simplest setup, one machine runs everything: the web app, the database, and the cache.

👤 User browser / app request response Single Server Web application Database Cache 🌐 DNS name → IP address

The user's browser resolves your domain through DNS, sends an HTTP request, and the one server returns a response. Simple to reason about — and fragile, because there is nothing behind it.

Why begin here? Every scaling decision later in the course is a reaction to a limit this setup hits. Knowing the baseline is what lets you justify each new box you add.
Foundations · 02

Where the data lives

Storage selection comes down to two families. Pick by the shape of your data and the access pattern, not by hype.

Relational databases (RDBMS) structure data into tables — like spreadsheets. Columns are the fields/attributes; rows are individual records. They use SQL to query and manipulate that data, and they shine when relationships and consistency matter.

Best for

Structured data, clear relationships, transactions that must stay consistent (payments, orders, accounts).

Examples

PostgreSQL MySQL Oracle SQLite

-- a customers table: columns = fields, rows = records
SELECT id, name, email FROM customers WHERE id = 123;
-- 123 | John | john@mail.com

NoSQL databases trade rigid schemas for flexibility and fast access to large volumes of unstructured data. There are four common types — each solves a different problem.

📄Document

Flexible JSON-like docs. MongoDB.

🪵Wide-column

Massive scale, write-heavy. Cassandra.

🕸Graph

Entities + relationships. Neo4j, Neptune.

Key-value

In-RAM, blazing fast. Redis, Memcached.

Real example: Amazon runs the Neptune graph DB to power product recommendations from your past orders — relationships are the whole point, so a graph store fits.
In your stackThalamus runs on Supabase Postgres — relational, and that's the right call: goals, votes, profiles and providers are full of relationships. Your static dashboards (Command Center and friends) have no database at all — the JSON files in the repo (money.json, plan.json…) are the "database".
Foundations · 03

Two ways to handle more traffic

When one server can't keep up, you scale. There are exactly two directions — and the difference between them decides your reliability story.

Add power to one server

Bolt on more RAM, CPU or other resources to your existing machine. Dead simple, and fine for low-to-moderate traffic.

  • Resource limits — there's a hard cap on how big one box can get. You will hit the ceiling.
  • No redundancy — if that one server dies, the whole app goes down with it.
one bigger server +RAM +CPU

Add more servers

Spread the load across many machines that share the work. This is what high-traffic systems do — it needs a load balancer in front, but it buys you redundancy and near-unlimited room to grow.

  • Redundant — one server failing doesn't take the app down.
  • Elastic — add a 4th, 5th, Nth server whenever you need.
LB
In your stackYou never hand-scale. Vercel serverless + managed Postgres scale horizontally for you; the static dashboards sit on a CDN and scale essentially without limit. Your job is the architecture, not the boxes.
Foundations · 04

The traffic cop: load balancing

The box sitting between clients and your servers is the load balancer. It distributes incoming requests across your pool, sending each to whichever server has the least load. It does three jobs at once:

⚖️ Distribute

Spreads traffic evenly so no single server gets overwhelmed.

🛟 Fault tolerance

If a server goes down, it stops routing there and shifts traffic to the rest.

📈 Scalability

Add a new server and the LB folds it into rotation automatically.

How does it pick a server?

Click an algorithm to see how it routes:

Health checks — how the LB knows a server died

The load balancer constantly pings each server with a health-check request. The moment a server stops answering, the LB marks it offline and routes nothing to it — until a later health check succeeds and it's back in rotation. No human in the loop.

In practice: Nginx is the most common software load balancer; cloud platforms ship managed ones like AWS ELB and Google Cloud Load Balancing.
In your stackYou don't run a load balancer. Vercel's edge distributes requests and health-checks globally — your middleware already runs in ~8 regions. Nothing to configure or babysit.
Foundations · 05

The single point of failure

A single point of failure (SPOF) is any one component whose failure brings the entire system down. Classic case: many API servers, but all of them talk to one database.

clients load bal. API 1 API 2 API 3 💥 Database single & shared = SPOF

If that database dies, none of the APIs can read or write, and every client gets nothing back. SPOFs are dangerous because they concentrate risk — the fix is redundancy: replicate the database, run multiple load balancers, and remove any box that the whole system leans on alone.

In your stackYour real single points of failure: the one Supabase project (all Thalamus data lives there), Vercel (every deploy routes through it), and .env (lose those secrets and deploys + integrations break). None has a backup today — that's the top item in the audit below.
API Design · 06

An API is a contract

API stands for Application Programming Interface — it defines how software components talk to each other. On one side a client (browser, mobile app); on the other a server that responds. The API is the agreement between them:

  • What requests can be made — which endpoints exist
  • What methods are allowed on each endpoint
  • What the request and response should look like
Think of it like a restaurant menu: it tells you what you can order and in what form it arrives — without exposing how the kitchen works.
In your stackThalamus exposes Next route handlers (e.g. /api/admin/business). And your WAT tools/*.py scripts are your internal API — the workflows/ markdown files are their contracts. Same idea as a web API, different runtime.
API Design · 07

Protocols & the transport layer

Application-layer protocols sit at the top of the network stack, built on transport-layer protocols below them. When building APIs you mostly care about the application layer — but it helps to know what's underneath.

🚚 TCP reliable

Connection-oriented. Guarantees ordered, complete delivery. The default for APIs and the web.

🏎 UDP fast

Connectionless, no delivery guarantees — but low latency. Good for streaming, gaming, real-time.

Application-layer protocols

HTTP / HTTPS

The foundation of web APIs. Client sends a request (method, headers, body); server returns a response (status code, content-type, body). HTTPS = HTTP + TLS/SSL encryption.

WebSockets

Persistent, bidirectional connection for real-time data — chat, live video, collaborative apps.

gRPC

Google's RPC protocol. Fast and compact — popular for service-to-service calls in microservices.

AMQP

Advanced Message Queuing Protocol — for asynchronous, queue-based communication.

Always use HTTPS. TLS/SSL encrypts data in transit, gives you data integrity, authenticates the server, and even helps SEO. Plain HTTP exposes everything on the wire — there is no good reason to ship it in production.

The HTTP request/response cycle

// REQUEST
GET /products  HTTP/1.1
Host: api.shop.com
Authorization: Bearer <token>

// RESPONSE
HTTP/1.1 200 OK            // 2xx ok · 4xx client error · 5xx server error
Content-Type: application/json
Cache-Control: max-age=60
In your stackEverything you ship is HTTPS over Vercel by default. No WebSockets or gRPC yet — you haven't needed real-time or service-to-service calls. When Thalamus adds something live (chat, presence), that's the WebSocket moment.
API Design · 08

API styles: REST, GraphQL, gRPC

The most common style for web and mobile. REST is stateless — every request carries everything needed to process it, so no prior request is required. It maps cleanly onto standard HTTP methods, and HTTP status codes pair beautifully with CRUD operations.

Click a method to explore

Idempotency means calling the same request 2 or 3 times gives the same result. GET is safe & idempotent. POST is not — call it twice and you create two resources.

GraphQL is a query language — the second most common style after REST. It exposes a single endpoint and lets the client ask for exactly the fields it needs, no more, no less. It runs over HTTP. Operations are queries (read) and mutations (write).

query {
  product(id: 301) {
    title          # ask for only what you need
    price
  }
}

Solves REST's over-fetching / under-fetching problem — but adds query-complexity and caching challenges.

gRPC uses compact binary messages over HTTP/2, which makes it much faster than JSON-over-HTTP. It's the go-to for microservice-to-microservice calls inside your architecture, where speed matters more than human-readability.

✅ Fast

Binary payloads, multiplexed streams.

✅ Typed

Strict contracts via protobuf.

⚠️ Less human-readable

Harder to debug by eye than REST.

Protocol choice shapes the API

The protocol you pick affects structure, performance and capabilities — so match it to the job:

  • REST → HTTP + status codes, ideal for CRUD over the web
  • Real-time APIs → WebSockets for chat / streaming
  • Microservices → gRPC for low-latency internal calls
In your stackYour route handlers already follow these verbs and return real status codes — the admin route answers 401 (not logged in) and 403 (not allowed). REST is the right fit at your size; you don't need GraphQL or gRPC.
API Design · 09

Designing APIs that hold up

Two pillars the course stresses for production-grade APIs:

🔒 Security

  • Authenticate & authorize every caller
  • Validate all inputs
  • Apply rate limiting

⚡ Performance

  • Cache where it helps
  • Paginate large lists with limit & offset — never return thousands of rows at once
  • Minimize payloads & reduce round trips
// pagination keeps responses small and fast
GET /products?page=2&limit=3
// → { page: 2, limit: 3, total: 10, products: [...] }
Security · 10

Authentication — who are you?

Authentication answers one question: who is the user trying to access the system? Before anyone reaches your API gateway, services, or data, they must prove their identity via a login request. The course untangles a lot of commonly-confused terms:

MYTH "JWT is an authentication method" +
FACT  JWT is just a token format — a way to encode claims. It's not an auth method by itself.
MYTH "Bearer auth and JWT are the same thing" +
FACT  Bearer is how you send a token in the header; JWT is what the token is. A bearer token may or may not be a JWT.
MYTH "OAuth 2 is an authentication method" +
FACT  OAuth 2 is an authorization framework — about granting access, not proving identity.
MYTH "SSO is an authentication method" +
FACT  Single sign-on is a UX pattern — one login across many apps — layered on top of real auth methods.

The methods, lightest to heaviest

Basic

Username + password in the header. Simple, weak alone.

API keys

A static secret identifying a caller/app.

Sessions + cookies

Server stores session state; cookie holds the id.

Bearer / JWT

A signed token sent on each request.

Access & refresh tokens

Modern systems issue two tokens at login:

short-lived · 15 min – 1 h

Used for actual API calls. Short lifetime limits the damage if it leaks.

long-lived · days – weeks

Used only to mint new access tokens when they expire. Store it in an HTTP-only cookie — never in local storage, so client-side scripts can't steal it.

Why JWTs scale: stateless verification

💻 Client 🔑 Auth server 🗄 API server 1 · login → returns JWT 2 · request with Bearer token 3 · verify signature locally — no DB lookup

Because the token is signed, the API server validates it with the signature alone — no round-trip to a session store. That statelessness is exactly what lets you add servers freely behind a load balancer.

Higher up the stack: OAuth 2 (delegated authorization), OpenID Connect (identity on top of OAuth), and SSO (one login, many apps).

In your stackThalamus uses Supabase auth (createServerClient + getUser, cookie-backed, JWT under the hood) — exactly the stateless story above. Your dashboards use a lighter gate: a client-side passphrase (AES-GCM, KB_PASSPHRASE) for the encrypted ones, or edge basic-auth for the private decks and the site's beta gate.
Security · 11

Authorization — what can you do?

Once we know who you are, authorization decides what you're allowed to touch. The workhorse pattern is Role-Based Access Control (RBAC): users get roles, and each role carries a fixed set of permissions.

RoleReadCreate / UpdateDeleteManage users
Admin
Editor
Viewer
Same pattern you see on GitHub: an admin manages the repo and people; an editor changes content but can't delete or manage members; a viewer can only read.
In your stackYour RBAC is Postgres RLS policies (own-profile only; admin via is_admin()). The admin route gates with 401/403, and writes use a service-role client that bypasses RLS — powerful, so that key must stay server-side only. (The code itself notes the admin check is still "simple".)
Security · 12

Securing the API surface

Beyond auth, four habits keep an API from getting breached:

🔐 HTTPS everywhere

TLS/SSL encrypts traffic in transit so nothing travels in the clear.

🌍 CORS

Cross-Origin Resource Sharing controls which domains may call your API from a browser. Without it, a malicious site could ride a user's browser to hit your API. Allow only your own front end.

💉 Injection prevention

SQL / NoSQL injection happens when user input is dropped straight into a query. Validate & parameterize everything.

🚦 Rate limiting

Cap requests per client to blunt abuse, scraping and brute-force attacks.

In your stackHTTPS everywhere, Next handles CORS, and RLS is your real boundary (the anon key is public by design). The site's only wall right now is the beta password. Migration 008 already closed two "world-readable" RLS holes — the audit below lists what's left to check.
Your systems · audit

Now point it at your stack

The same lenses, turned on what you actually run — grounded in your repo, not generic advice. Reassuring news first: the architecture is sound. The flags below are mostly "confirm" and "back this up", not "rebuild".

Thalamus app

Next 16 · Supabase (Postgres + auth + RLS) · Vercel
  • Relational Postgres fits the data — goals, votes, providers and profiles are all relationships.
  • Stateless Supabase auth (cookie / JWT) — scales cleanly behind the edge.
  • RLS hardening (migration 008) closed two "world-readable" holes; the app already reads the safe verification_public view.
  • Confirm migrations 004–008 are actually applied in the live Supabase — the app depends on that view (if a provider page 500s, they're not applied).
  • The admin check is "simple" (its own code comment) — formalize roles before adding more admins.
  • The service-role key bypasses RLS — must stay server-side only, never NEXT_PUBLIC_.

ilonagolman.com

Next (mostly static) · edge basic-auth gate
  • Tiny attack surface — mostly static pages on a CDN.
  • One env var flips public ↔ private (PREVIEW_PASSWORD) — no code change to launch.
  • That beta password is the only wall; opening to the world = remove it + redeploy.
  • DNS still pending — the domain 404s; the stable link is the Vercel alias for now.

Command Center + dashboards

Static HTML on Vercel · some AES-GCM encrypted
  • No server, no DB → almost nothing to attack.
  • Encrypted ones decrypt client-side with KB_PASSPHRASE — the data never sits in plaintext on the host.
  • Client-side crypto means the ciphertext is public — security = passphrase strength + never leaking it.
  • Data lives as repo JSON (version-controlled) — keep PII like lead names out of it, as you already do.

WAT tooling + secrets

Python tools/ · workflows as contracts · .env
  • Deterministic scripts, version-controlled, easy to re-run — the WAT split is doing its job.
  • .env is your single secrets SPOF (Supabase, Vercel, Cloudflare, Stitch, GitHub tokens) — no backup, no redundancy.
  • Rotate STITCH_API_KEY if it was ever shared or exposed.

What to check first

  1. Confirm Supabase migrations 004–008 are live (the app reads verification_public — a 500 on a provider page means they're not applied).
  2. Verify no service-role / secret key is exposed as NEXT_PUBLIC_ anywhere in the frontend.
  3. Rotate STITCH_API_KEY if it ever left your machine.
  4. Make a secure backup of .env — it's your single secrets point of failure.
  5. Decide the beta-gate → public flip (DNS + remove PREVIEW_PASSWORD) when you're ready to launch.
The pattern: you've quietly outsourced scaling, load balancing and failover to Vercel + Supabase — which is exactly right for a solo operator. What's left for you is the part platforms can't do: the data model, the access rules (RLS), and guarding the secrets.
Wrap-up · 13

Why this is the skill to learn now

AI writes most of the implementation now, so interviews have shifted. The system-design round tests something AI can't fake for you: whether you understand how components fit together at a high level, can make architectural decisions at scale, and can articulate the trade-offs of what you'd build.

The throughline of the whole course: start simple, add a component only when a real limit forces you to, and always be able to explain why — what you gained, and what it cost you.

Want the full 2-hour walkthrough with every diagram?

▶ Watch on YouTube — Hayk Simonyan