An AI‑powered system design interview coach, study planner, and resume assistant built with Node.js, TypeScript, Postgres, and the OpenAI API.
The goal of this project is to simulate a realistic FAANG‑style system design prep experience:
- generate tailored system design questions,
- evaluate your answers with structured feedback,
- track your strengths and weaknesses over time,
- recommend resources and a personalized study plan.
- Overview
- Key Features
- Architecture
- Data Model
- API Overview
- Front‑End UI
- Environment & Setup
- Development Scripts
- How the AI Pieces Work
- LLM Usage, Costs & Metrics
- Roadmap
System Design Co‑Pilot is a full‑stack application that helps you prepare for system design interviews by acting as:
- a question generator that adapts to your weak and strong areas,
- an AI evaluator that scores and critiques your design answers,
- a coach that spots recurring patterns in your mistakes and suggests how to fix your mental model, and
- a study planner that converts your stats into an actionable plan with recommended resources.
- a resume assistant that analyzes and tailors your resume for system design–heavy backend roles.
The stack is intentionally close to what you’d expect in a modern backend/AI service:
- Backend: Node.js, TypeScript, Express‑style routing
- Database: Postgres (Supabase‑hosted)
- Cache: Redis / Upstash (used to cache AI outputs like coach feedback and study plans)
- AI: OpenAI API with strict JSON responses validated via Zod
- Frontend: Simple HTML/JS client (
public/index.html) for manual exploration
- Generates topic‑ and difficulty‑aware system design questions.
- Topics include things like caching, feeds, messaging queues, rate limiting, search, payments, observability, etc.
- Difficulty is tracked (
easy | medium | hard) and can evolve based on performance.
- You write a free‑form answer to a system design prompt.
- The backend calls the OpenAI API to produce structured feedback:
score(0–10)strengths(what went well)weaknesses(gaps in the answer)
This is stored as a system_design_session for later stats and analysis.
For a given session, the coach endpoint returns:
- A short summary of how you did.
- What you did well in this specific answer.
- What to improve next time.
- Consistent patterns across sessions for this topic (e.g., “you often forget failure modes”).
- Mental model fixes: concrete bullet points on how you should change your thinking for this topic.
- Next practice suggestion (topic + difficulty + reason).
- Recommended resources pulled from the internal knowledge base.
This is essentially an “AI senior engineer” giving recurring feedback shaped by your history, not just the latest answer.
The stats endpoint summarizes a user’s practice over time, including:
- total sessions
- answered sessions
- average score
- last practice time
- overall level (
strong | needs_improvement | limited_data) - per‑topic stats:
- number of sessions
- average score
- topic label (
strong | weak)
This is used by both the coach and the study planner.
The study plan endpoint uses:
- user stats (overall + per‑topic),
- lists of weak/strong topics,
- and topic‑specific resources
to generate a JSON study plan:
profileSummaryfocusTopicsrecommendedSequence(steps with topic + difficulty + goals)practiceSuggestions(concrete actions)
The UI renders this nicely and turns referenced resources into clickable links.
The project defines a small set of system design resources in sd_resources:
- Caching Strategy Cheat Sheet
- Designing a Global Feed
- Messaging Queue Trade‑offs
- Rate Limiting Cookbook
- Search Ranking Primer
- Payments Consistency Notes
- Observability for SD Interviews
Each resource has:
titletopicurl(served frompublic/*.html)content/ snippet
These are used as a knowledge base:
- The coach can reference them when giving feedback.
- The study plan can point to them as next‑step material.
- The UI shows them as links (no raw
.htmlfilenames).
The backend also supports semantic retrieval using embeddings + pgvector:
- A seed script writes these resources into
sd_resourcesand can attach embeddings to each row (when OpenAI quota allows). - At runtime, the coach and study plan build a small query (topic + question/weaknesses or topic + stats) and:
- embed it via OpenAI,
- run a pgvector similarity search to pick the most relevant resources for that topic,
- fall back to simple topic filtering if embeddings are missing.
The resume assistant lives alongside the system design coach and focuses on making your profile read like a strong backend / system design engineer:
- Resume analysis: parses your resume into structured sections (header, summary, skills, experience, education, projects).
- Issue detection: highlights missing signals for senior / FAANG‑style roles (lack of impact, weak system design keywords, no leadership, etc.).
- Improved bullets: suggests stronger, impact‑driven bullets for key experience lines.
- Job‑specific tailoring: given a job description + target role, generates an ATS‑friendly, tailored version of your summary and experience while preserving truthfulness.
- Uses the same OpenAI JSON + Zod pipeline as the system design agents, so responses are strongly typed and safe to render.
At a high level the system looks like this:
-
Client (Browser)
public/index.htmlwith vanilla JS- Calls REST endpoints to:
- create/find user by email
- get next question
- submit answer
- fetch coach feedback
- fetch user stats
- generate a study plan
-
API Server (Node.js + TypeScript)
- Express‑style routes under
/api/v1 - Route modules:
routes/users.tsroutes/system-design.ts
- Service layer:
users.service.tssystem-design.service.tssystem-design-ai.service.tssd-resources.service.ts
- DAO layer:
users.dao.tssystem-design.dao.tssd-resources.dao.ts
- Infra:
db.ts(Postgres connection + query helper)redis(Upstash-backed cache for AI responses and plans)
- Express‑style routes under
-
Database (Postgres)
users_tblsystem_design_sessionssd_resources- (Optionally) future table(s) for embeddings
-
AI Integration
openAiClient/responsesClientwrapper- Strongly typed JSON outputs checked via Zod schemas
- Different prompts & schemas for:
- question generation
- answer evaluation
- coaching
- study plan
Minimal user record keyed by email:
id(UUID, primary key)email(unique)- timestamps
One row per question/answer session:
id(UUID)user_id(FK →users_tbl.id)topic(text, e.g.caching,queues)difficulty(easy | medium | hard)question(text)answer(text, nullable until user submits)score(numeric, nullable)strengths(JSON/text array)weaknesses(JSON/text array)created_at,updated_at
System design knowledge base:
id(UUID)title(text)topic(text)url(text, served frompublic/*.html)contentorcontent_snippet(text)- (future)
embedding(vector) for pgvector
These tables are managed via SQL in Postgres (e.g., Supabase) and accessed via the DAO layer.
Base path: /api/v1
-
POST /api/v1/users- Create or find user by email.
- Request body:
{ "email": "user@example.com" } - Response: user record (id, email, timestamps).
-
GET /api/v1/users/:email/system-design-stats- Returns aggregated system design stats for this user.
-
GET /api/v1/users/:email/system-design-study-plan- Returns AI‑generated study plan JSON for this user.
-
POST /api/v1/system-design/session- Creates a new session (question) for a user.
- Can support simple modes like:
- user asks for a topic + difficulty, or
- “auto” topic based on weak areas.
-
GET /api/v1/system-design/session/:id- Fetch full session details (question, answer, score, etc.).
-
POST /api/v1/system-design/submit-answer- Submits an answer for a session and runs AI evaluation.
- Returns updated session with
score,strengths, andweaknesses.
-
POST /api/v1/system-design/coach- Takes
email+sessionId. - Calls AI to generate:
- summary
- what you did well
- what to improve next time
- consistent patterns
- mental model fixes
- next practice suggestion
- recommended resources
- Takes
The UI is a single HTML page (public/index.html) with minimal styling and vanilla JS.
It supports:
- entering your email (used as the identity key),
- generating the next system design question,
- typing your answer,
- submitting to get:
- AI score
- strengths/weaknesses
- coach feedback
- viewing your system design stats,
- generating a study plan,
- clicking into recommended resources (served from
public/*.html).
The UI is intentionally simple: it’s designed to showcase the backend + AI flows, not be a polished production UI.
There is a separate single‑page UI for the resume assistant:
-
Analyze Resume
- Paste plain‑text resume content or upload a PDF/DOCX.
- Optionally provide a target role and target company.
- Calls the backend to run
resume-ai.serviceand returns:- parsed structure (name, headline, skills, experience, education),
- issues & gaps,
- section‑level issues,
- improved sample bullets.
-
Tailor to Job
- Uses your pasted/uploaded resume plus a job description to:
- rewrite the summary toward the target role,
- reshape skills to better match the stack,
- rewrite experience bullets to emphasize relevant impact,
- return a full, ATS‑ready resume text block you can copy out.
- Uses your pasted/uploaded resume plus a job description to:
-
Upload support
/api/v1/resume/extract-textaccepts file uploads (PDF/DOCX) usingmulter.- The server uses
pdf-parseandmammothto extract text safely on the backend before sending it to the OpenAI API.
This project also exposes its core capabilities as Model Context Protocol (MCP) servers so that tools like Cursor, Claude Desktop, and (eventually) ChatGPT Desktop can call into your backend with natural language.
The system design MCP server (commonly configured with an ID like sd-copilot-mcp) exposes tools that let an LLM act as a thin client over your existing APIs and services:
sd_recommend_resources– given a user email, returns weak topics plus recommended system design resources grouped by topic.sd_next_question– given a user email, returns the next practice question, topic, difficulty, and session ID using the same logic as/system-designin the API.sd_study_plan– given a user email, returns the AI‑generated study plan JSON (profile summary, focus topics, recommended sequence, practice suggestions).
Typical usage from an MCP‑aware client (e.g., Cursor):
- Configure the MCP server to run
npm run mcp:sd. - In a chat, say something like:
“Use the system design MCP to generate my next question fortest@example.comand then summarize how I should approach it.”
The client will:- detect that
sd_next_questionis the right tool, - call it with
{ "email": "test@example.com" }, - feed the JSON result back into the LLM for explanation.
- detect that
The resume MCP server exposes tools to analyze and tailor resumes without going through the browser UI:
- One tool for resume analysis (takes
resumeText, optionaltargetRole, optionaltargetCompany, optionaljobDescription) and returns the sameResumeAnalysisJSON used by the web UI. - One tool for resume tailoring (takes
resumeText+jobDescription+ optional targeting fields) and returns:rewrittenSummaryrewrittenSkillsrewrittenExperiencenotesForUserfullResumeText(ATS‑friendly draft)
Example natural‑language usage in an MCP client:
- “Analyze this resume text for a Senior Backend role at a FinTech in Austin using the resume MCP.”
- “Tailor my existing resume to this job description using the resume tailoring MCP tool.”
The client interprets these instructions, chooses the right MCP tool, and passes structured arguments (e.g., JSON with resumeText and jobDescription), then uses the JSON response to drive further reasoning or UI.
- Node.js (v18+ recommended)
- npm or pnpm
- A Postgres instance (e.g., Supabase)
- (Optional) Redis / Upstash
- An OpenAI API key
Create a .env file in the project root, with values like:
# Postgres connection string
DATABASE_URL=postgresql://postgres:password@host:5432/postgres
# Redis (Upstash, used for caching AI outputs)
UPSTASH_REDIS_REST_URL=...
UPSTASH_REDIS_REST_TOKEN=...
# Optional debug flags
CACHE_DEBUG=0
AI_LOG_DEBUG=0
# OpenAI
OPENAI_API_KEY=sk-...
# Server
PORT=3000npm install
# or
pnpm installMake sure your Postgres database has the tables described in the Data Model section (these can be created via SQL migrations / Supabase SQL editor).
Common scripts (names may vary based on your package.json):
# Start the dev server (ts-node)
npm run dev
# Seed system design resources (sd_resources)
npm run seed:sdThe seed script populates sd_resources with initial entries and (optionally) embeddings or content snippets.
The project wraps the OpenAI API in a small client that:
- enforces model selection (
gpt-4.1-mini, etc.), - expects structured JSON outputs,
- validates outputs with Zod schemas (e.g.,
CoachFeedbackSchema,StudyPlanSchema), - surfaces typed results back to the service layer.
This ensures that the rest of the app deals with proper TypeScript types instead of arbitrary strings.
While not using a formal agent framework, the project defines clear “agent‑like” responsibilities:
-
Question Agent
Picks topic + difficulty and generates a system design prompt. -
Evaluator Agent
Takes question + answer and produces score + strengths + weaknesses. -
Coach Agent
Uses:- current session,
- user stats,
- topic mistake patterns,
- and resources to produce:
- summary,
- improvement advice,
- consistent patterns,
- mental model fixes,
- next practice suggestion,
- recommended resources.
-
Study Plan Agent
Uses aggregated stats + resources to create a multi‑step study plan (focus topics, sequence, practice suggestions).
These are implemented as service functions that call the OpenAI client with specific prompts and schemas.
This project treats LLM usage and cost as first‑class production concerns, not an afterthought.
All OpenAI calls (questions, coach feedback, next‑topic suggestions, study plans, embeddings for RAG, etc.) go through a single client in src/infra/openaiClient.ts.
At that boundary, the client:
- wraps each OpenAI call in a timing helper (
timeOpenAiCall) to log latency and normalize errors, and - records token usage and approximate cost per model via an in‑memory accumulator:
callspromptTokenscompletionTokenstotalTokenscostUsd
Internally, the client looks at the usage block on OpenAI responses (e.g. prompt_tokens, completion_tokens, total_tokens) and multiplies by a small model pricing table:
const MODEL_PRICING_USD: Record<string, { inputUsdPer1K: number; outputUsdPer1K?: number }> = {
"gpt-5": { inputUsdPer1K: 0, outputUsdPer1K: 0 },
"gpt-4.1-mini": { inputUsdPer1K: 0, outputUsdPer1K: 0 },
"text-embedding-3-small": { inputUsdPer1K: 0 },
};Configure this for your environment:
Replace the zero values with the current per‑1K token prices from OpenAI’s pricing page before using these numbers for real cost reporting.
Because this logic lives in the shared client (not in individual services like system-design-ai.service.ts or coding-ai.service.ts), new features automatically participate in usage tracking with no additional plumbing.
To make these stats observable without attaching a full metrics stack, the project exposes a small internal endpoint under the existing health router:
- Route:
GET /api/v1/health/metrics/openai - Auth: Protected by the same
X-Internal-Api-Keymiddleware as other/api/v1routes.
Sample response shape:
{
"source": "openai_client",
"models": {
"gpt-5": {
"calls": 12,
"promptTokens": 8450,
"completionTokens": 3920,
"totalTokens": 12370,
"costUsd": 0.48
},
"text-embedding-3-small": {
"calls": 30,
"promptTokens": 9100,
"completionTokens": 0,
"totalTokens": 9100,
"costUsd": 0.02
}
}
}- Dashboards / alerts
- Point a lightweight scraper or Prometheus job at
/api/v1/health/metrics/openai(with the internal API key) to track:- token usage per model,
- total estimated cost per process,
- spikes in call volume or cost.
- Point a lightweight scraper or Prometheus job at
- Per‑feature attribution (optional extension)
- The current implementation aggregates per model.
- In a production system, you can extend the client to accept a
featureorcallerlabel (e.g.,system-design-question,sd-coach,resume-analyze) and emit per‑feature metrics (e.g., counters in Prometheus or aopenai_usagetable in Postgres).
- Budget enforcement (optional extension)
- With a small wrapper around the client, you can reject or degrade non‑critical requests once a daily/monthly budget threshold is crossed (e.g., “don’t run resume‑tailoring when we’re above 90% of our LLM budget, but keep core system design coaching alive”).
Current behavior:
sd_resourcesstores labeled notes per topic, along with an optionalembeddingcolumn (pgvector).- A seed script populates
sd_resourcesand, when OpenAI quota is available, generates embeddings for each resource. - For the coach:
- query text includes topic, question, and user weaknesses / mistake patterns.
- For the study plan:
- query text includes topic, overall level, and average score.
At runtime:
-
Build a query string from topic + context (question, weaknesses, stats).
-
Create an embedding using OpenAI.
-
Run a pgvector similarity search:
SELECT id, title, topic, url, content, created_at FROM sd_resources WHERE topic = $1 AND embedding IS NOT NULL ORDER BY embedding <-> $2::vector LIMIT $3;
-
Use the top results to:
- provide grounded references for the coach feedback, and
- inform the weak‑topic resources in the study plan.
If embeddings are missing or an error occurs, the system gracefully falls back to simple topic‑based resource selection.
Future enhancements:
- Add more resources (and richer content) per topic.
- Support multi‑topic retrieval in a single call.
- Add UI to browse the underlying knowledge base directly.
A few planned / potential enhancements:
-
Extend RAG with pgvector
- ✅ Initial embeddings + semantic search for coach and study plan.
- ⏩ Expand coverage (more resources, multi‑topic queries, richer snippets).
-
History UI & Session History API
- Paginated list of sessions per user.
- Detail view: question, answer, score, coach feedback.
- Trend charts over time (scores per topic).
-
Redis Caching
- ✅ Cache study plans per user keyed by latest practice time.
- ✅ Cache coach feedback per session/score so repeated requests are free.
- ⏩ Refine TTLs and invalidation strategies as the app scales.
-
Auth & Multi‑user Deployment
- Simple auth (e.g., magic link or OAuth) instead of free‑text email.
- Hosted deployment (Render/Fly/Railway/etc.) behind a custom domain.
-
Agent Framework & MCP Integration
- The current design already has clear “agents by responsibility” (question, evaluator, coach, study planner, resume assistant).
- ✅ Initial MCP servers for:
- system design (study plan, next question, resource recommendations),
- resume analysis & tailoring.
- ⏩ Future: deeper LangGraph integration and richer MCP tool coverage (e.g., full session history, interactive chat‑style coaching).
This project is meant to be both a learning playground for AI‑powered system design coaching and a demonstration of how to build an end‑to‑end, production‑flavored AI feature with a real backend, database, and UI.
#Kubernetes Instruction: kubectl apply -f k8s/namespace.yaml kubectl apply -f k8s/deployment.yaml kubectl apply -f k8s/service.yaml kubectl apply -f k8s/ingress.yaml |