- What it is
- Gamified interview-prep prototype, selected for Microsoft for Startups
- Year
- 2026
- Stack
- Next.js 16React 19TursoGemini + GitHub + OpenAIMCPOAuth 2.1
- Go deeper
- System architecture
Context
I watched classmates in my graduate program lose offers because nobody had mapped their resume against what the role actually asked for. Reading a job description takes minutes. Matching a resume against it takes hours of guessing at what a hiring manager will weigh.
I wanted a prototype that does that mapping in one pass, then turns the gaps into practice people come back to. It was also a way to put my Northwestern product management coursework to work and to close a gap of my own. My background is backend ML, and I had never built something end to end, interface included.
Action
- Evidence first, then a score: The model pulls evidence from the resume, but the scoring itself is deterministic, and every finding links to the resume line it rests on. A candidate can check the report instead of having to trust it.
- Practice built from the gaps: The gap list feeds role-specific flash cards, a scored multi-turn mock interview, points, streaks and 14 badges. A candidate moves from report to drill to interview without typing anything twice.
- A fallback for every model call: Gemini returned three 503 errors in a row in production. I put GitHub Models and OpenAI behind the same interface, each with three retries and a short backoff.
- The analysis off the request path: On Vercel, the analysis was cut off whenever the function shut down after sending its response. I moved it into Next.js post-response execution and ran the function in the same region as the Turso database.
- The same analysis inside AI assistants: An MCP server gives Claude, Gemini, ChatGPT and Cursor the role-fit analysis and taxonomy lookup, secured with OAuth 2.1, PKCE and dynamic client registration.
Result
- About 3x faster: An analysis that took 90 to 150 seconds, when it finished at all, now takes 31 seconds in production (30.8 cold, 32.2 warm). That is within 1.5 seconds of my local machine.
- A provider outage no longer ends a session: I forced the primary model to fail in production and confirmed a clean hand-off to each fallback, with reports that matched the primary path.
- New roles without new code: Finance, product management and design roles were added as catalog entries. The engine did not change.
- A security hole closed before sign-in went public: A smoke test showed that a raw user ID in the session cookie would grant admin access. I added signed cookies the same afternoon, and three forgery tests keep it fixed.
- Selected for Microsoft for Startups.
Learnings
With Claude Code and Codex I could try a few directions in parallel before settling on one, so typing code stopped being what slowed me down. Deciding which direction to keep became the actual work. I also built a few agents of my own for narrow tasks along the way.
The slowdown taught me to measure before touching anything. Everything ran fine on my machine, and "production is slower" stayed a hunch until a side-by-side Playwright run and timing logs at every stage pointed at the deployment itself.
As a backend engineer I would normally have taken a component library's defaults. Here I designed the interface myself, with the people using it in mind, and the gamification came out of that: a gap report gets read once, but flash cards, streaks and reminders give someone a reason to come back tomorrow.
If I picked it up again, I would split the analysis into smaller prompts that run in parallel, or stream findings to the page as they arrive. About 95% of the remaining wait is the model call itself.