System Design8 min readMay 15, 2025

Building IntelliPath: How I Built an AI Career Guidance Platform

A deep dive into building IntelliPath — from designing the recommendation engine to managing PostgreSQL with Prisma, handling background jobs with Inngest, and deploying on Vercel.

Next.jsAIPrismaSupabaseSystem Design

Why I Built IntelliPath

Every CS student I know has faced the same problem: you know you want to become a developer, but you have no idea which skills matter most, in what order to learn them, or which opportunities align with your current level.

I built IntelliPath to solve exactly that. It's an AI-powered career guidance platform that analyzes your skills, identifies gaps, and generates personalized roadmaps with real learning resources, internship recommendations, and mock interview prep.

The Architecture

IntelliPath is built on Next.js 15 App Router — a decision I made early because of server components, server actions, and the clean separation between client/server code it enables.

Database Layer

The database is PostgreSQL hosted on Supabase, managed with Prisma ORM. Here's the core schema design decision I made:

// Key Prisma models
model User {
  id           String   @id @default(cuid())
  clerkId      String   @unique
  skills       String[]
  careerGoal   String?
  assessments  Assessment[]
  roadmaps     Roadmap[]
  createdAt    DateTime @default(now())
}

model Roadmap {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  content   Json     // AI-generated roadmap as structured JSON
  skillGaps String[]
  createdAt DateTime @default(now())
}

I chose to store the AI-generated roadmap as Json in Postgres rather than a separate table, because the structure varies per user and querying it as a whole document made more sense than normalizing it.

Authentication

I used Clerk for authentication because it handles the hard parts — email verification, OAuth, session management — while integrating cleanly with Next.js middleware for route protection.

// middleware.ts
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher([
  '/dashboard(.*)',
  '/roadmap(.*)',
  '/resume(.*)',
]);

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) await auth.protect();
});

The Recommendation Engine

This was the hardest part. The recommendation engine works in 3 stages:

  • Skill Assessment: User selects their current skills from a curated list
  • Gap Analysis: We compare against target role requirements stored in the database
  • Roadmap Generation: An AI prompt with the gap data generates a structured learning path

The AI call uses a structured prompt that forces JSON output:

const prompt = `
You are a career advisor. Given these existing skills: ${userSkills.join(', ')}
And these skill gaps for a ${targetRole} role: ${gapSkills.join(', ')}

Generate a 12-week learning roadmap in this exact JSON format:
{
  "weeks": [
    {
      "week": 1,
      "focus": "...",
      "topics": ["..."],
      "resources": [{"title": "...", "url": "...", "type": "..."}]
    }
  ]
}
`;

Background Jobs with Inngest

Report generation (PDF roadmaps, email summaries) is offloaded to Inngest background functions. This prevents blocking the UI while heavy operations complete.

// inngest/functions.ts
export const generateRoadmapReport = inngest.createFunction(
  { id: "generate-roadmap-report" },
  { event: "roadmap/requested" },
  async ({ event, step }) => {
    const roadmap = await step.run("fetch-roadmap", async () => {
      return await db.roadmap.findUnique({ where: { id: event.data.roadmapId } });
    });

    await step.run("send-email", async () => {
      // Send the roadmap via email
    });
  }
);

Key Lessons Learned

1. Server Actions are a Game Changer

Using Next.js server actions instead of REST API routes for mutations eliminated a huge amount of boilerplate. Form submissions, data mutations, and cache revalidation all became much simpler.

2. Prisma Schema Migrations Need Care

Running prisma migrate dev in production (even on Supabase) can cause issues if you have existing data. I learned to always use prisma migrate deploy in production and preview migrations in staging first.

3. Type-Safe AI Responses

AI models sometimes return malformed JSON. I now always wrap AI responses in a try-catch with a Zod validation schema to ensure the data structure matches what my UI expects.

Results

IntelliPath is deployed on Vercel with PostgreSQL on Supabase. The full stack — from auth to AI generation to background jobs — runs in production with zero external servers.

💡 The biggest lesson: ship early, iterate fast. I spent too long perfecting the AI prompt instead of getting user feedback. The first version that shipped had a simpler recommendation algorithm — and it taught me more than weeks of solo optimization.