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.