From Exam Problems to Production AI
    Software Development

    From Exam Problems to Production AI

    Aman TiwariAman Tiwari
    April 20, 2026
    From Exam Problems to Production AI

    Full article

    What Two Months on a Real Platform Taught Me

    Two months ago, I was solving competitive exam problems. Today I’m shipping features on a production AI platform used by real users — including workflows that support partner restaurants and hiring pipelines — built with React, FastAPI, and multi-LLM orchestration. Here’s what the transition actually looked like from the inside.

    The Starting Point

    Before I joined, my AI exposure came from freelance work — running models, generating outputs, building small isolated tools. I could make things work, but only in a narrow sense: a script would run, an output would look right. What I hadn’t seen was a real system with real users, full-stack integration beyond a single file, or any of the operational concerns that keep a product alive: observability, cost control, failure handling.

    I think this is the default state of most junior AI engineers coming into the market right now. The question isn’t whether we’re smart. It’s how quickly we can close the distance between knowing the concepts and building something that keeps working once real users start interacting with it.

    Week One: Understanding the Ecosystem

    My first two weeks weren’t really about writing code. They were about trying to hold a map of the whole system in my head:

    •      Frontend: React 18 + Vite + React Router + TanStack Query + Zustand
    •      Backend: FastAPI (async)
    •      Database: Supabase (PostgreSQL + pgvector + Row Level Security)
    •      AI layer: dsPy with Anthropic + Gemini
    •      Workflows: Temporal
    •      Caching: Redis
    •      Observability: Langfuse + Sentry + structured logging
    •      Deployment: Railway

    That’s not a project. It’s an ecosystem. My learning loop became very simple: build, break, debug, repeat.

    That loop got real fast. The first thing I broke was a simple candidate update flow. I wired it with TanStack Query optimistic updates, so the UI showed the change instantly — but the backend request was failing silently due to a schema mismatch. The UI said the candidate was updated. The database said it wasn’t. I spent a while debugging the backend, assuming something was wrong there, before I realised the bug wasn’t really in either service. It was in the gap between what the frontend assumed and what the backend was actually doing. Coming from a linear mindset — run, result — that took some getting used to.

    From AI Outputs to AI Systems

    The biggest change for me wasn’t any single technology. It was how I thought about what I was building.

    Before, a task looked like: input → model → output. Now it looks like: user → React UI → API → AI pipeline → vector search → response → logging → monitoring. Every arrow in that chain is somewhere things can go wrong, and every box is something you have to reason about when they do.

    A few examples of what that means in practice on our platform. Our candidate processing pipeline parses CVs through Google Document AI for roughly 80% of standard cases, falls back to LlamaParse when the layout is complex, stores the result in Supabase with pgvector, and exposes semantic search on top of the embeddings. The AI email workflow generates messages through dsPy with Anthropic or Gemini, sends them via Resend, parses replies using mail-parser-reply, and runs in the background through Temporal so nothing depends on a request staying open. Observability and cost tracking sit across all of it — Langfuse for tracing, LiteLLM for token and cost accounting, Sentry for errors.

    Individually, none of these pieces are hard. The work is mostly in the seams between them.

    One example stuck with me. I was working on the candidate flow — CV parsing through Document AI and LlamaParse, storage in Supabase with pgvector, retrieval into the LLM pipeline. Every piece worked on its own. But the AI responses were off. Not broken, just wrong enough to be confusing. After tracing through Langfuse and the structured logs, I found it: the embeddings stored in pgvector didn’t match the structure the retrieval code expected, because the two parsing systems produced slightly different output formats. Nothing failed loudly — the system just degraded quietly.

    The Problems Tutorials Don’t Prepare You For

    Production AI surfaces a whole category of problems I hadn’t seen before — async failures in distributed Temporal workflows, retries and rate limits through Redis, structured logging so a request can be traced across services, securing data access with Row Level Security, and keeping LLM costs from quietly eating the margin as usage grows. These are the problems that separate a demo from a product.

    A small example that taught me a lot: a Temporal workflow for sending email kept retrying silently because of a bug in the reply-parsing step. The retries weren’t idempotent, so in edge cases we sent duplicate emails. Nothing crashed. Nothing showed up as an error in the UI. To understand what was actually happening, I had to trace logs across services, follow workflow execution, and reason about retry behaviour — none of which are things tutorials teach you.

    What Made the Ramp Work

    Looking back, I don’t think my starting skill level was the main variable. The environment was. Three things helped me more than anything I did on my own. Clear architectural guidance from the team lead, so I wasn’t guessing at system design while also learning the stack. Fast feedback loops — ship something small, get it reviewed, iterate — which kept me from polishing in private for too long. And a culture where questions were expected rather than merely tolerated, which mattered more than it sounds, because in the first few weeks I had a lot of them.

    One of those questions stood out later. Early on I asked, “Why can’t we just send this email inside the API instead of using Temporal?” It felt like I was missing something obvious. The answer turned out to unpack a whole set of decisions — why request lifecycles shouldn’t handle long-running tasks, why retries need to be durable, why workflows have to survive failures. That single conversation clarified more for me than hours of reading docs would have. At the time it felt like a basic question, but it turned out to touch a core system design decision.

    The goal was never getting things perfect. It was shipping, learning, and improving.

    Where I Am After Two Months

    The system hasn’t gotten simpler. I’ve gotten better at moving through it. I can follow a request end-to-end, debug across the stack, and ship features without needing someone looking over my shoulder for every decision.

    If I have to compress the takeaway into one thought: most of what separates knowing AI from shipping AI isn’t talent. It’s the environment you get to learn in.

    Written by

    Aman Tiwari

    Aman Tiwari

    Aman is an Product Engineer at Dutch Technology Frontiers, helping shape intelligent systems at the intersection of generative AI, retrieval, and product engineering. His work focuses on designing scalable decision-support experiences, intelligent automation layers, and reasoning systems that translate complex data into practical business outcomes. With experience spanning AI copilots, semantic research assistants, and context-aware decision systems, he brings a systems-thinking approach to building enterprise-grade intelligence products. Outside work, he enjoys exploring new AI frameworks, rapidly prototyping ambitious ideas, and translating cutting-edge research into polished product experiences.

    Connect on LinkedIn

    Ready to transform your organization?

    Let's discuss how we can help you build a competitive technology advantage.

    Contact Us