Building Scholaris: A Digital SAT Prep Platform
What Inspired Me
The idea for Scholaris came from a simple observation: test prep is broken. Tools like OnePrep (oneprep.xyz) showed me what a premium platform could look like, but they were expensive and out of reach for most students. I wanted to build something that combined that premium experience with a radical commitment to free access.
The mission was clear from day one:
$$ \text{Scholaris} = \text{Premium UX} + \text{Free access} + \text{AI-powered tutoring} $$
I saw students spending hundreds of dollars on tutoring and prep books, while open-source College Board question banks sat unused. The College Board had released 2,474 SAT questions through their OpenSAT dataset (1,443 English + 1,031 Math), and no one had built a beautiful, free platform around them. That gap became Scholaris.
I was also inspired by the idea of Scho, an AI tutor that would not just give answers but teach like a Socratic mentor. The prompt engineering behind Scho was deliberate: no immediate answers, LaTeX for math, warmth for students who were struggling.
What I Learned
1. Database Architecture Is Everything
The hardest lesson was realizing that a fragmented data layer kills velocity. Scholaris started with parallel systems:
practice_sessions+practice_attemptsfor practiceexam_sessions+question_attemptsfor exams- Hardcoded
questionBank.tsinstead of real database queries - Dual diagnostic result tables
Unifying these into a single repository layer (QuestionRepository, SessionRepository, DiagnosticRepository, ProgressRepository) was the single most impactful refactoring decision.
2. Supabase RLS Timeouts Are Real
Our Supabase project (aioyzumxxsepbnfivvdd) has strict statement timeouts:
$$ t_{\text{anon}} = 3\text{s}, \quad t_{\text{authenticated}} = 8\text{s} $$
Any query that tried to scan the full SAT question bank (6,181 Math questions, 4,225 English questions) with ORDER BY id would blow past these limits. I learned to use:
- Capped practice pools (
LIMIT 400unordered) - Cursor pagination instead of sequential
OFFSET SECURITY DEFINERRPCs with function-levelSET statement_timeoutoverrides
3. PostgREST Error 202 Is a Silent Killer
When the client called extended RPC signatures that the live database did not have, PostgREST returned PGRST202. The client treated this as "0 questions" and showed a "none passed quality checks" toast. The actual issue was that a migration for question_subtype had never been applied. This taught me to never assume a migration landed without explicit verification.
4. CSS Specificity Wars Are Real
The dark theme global rule:
.dark textarea { background: hsl(var(--background)) !important } /* (0,1,1) */
Built With
- css3
- desmos
- langchain
- next.js
- python
- react
- typescript
- youtube
Log in or sign up for Devpost to join the conversation.