Quiz Engine Data Model
A quiz engine supports timed assessments, randomization, auto-grading, leaderboards, and anti-cheat mechanisms.
Core Tables
Quiz (id, title, time_limit_seconds INT, randomize_questions BOOL,
randomize_options BOOL, pass_score INT, max_attempts INT,
show_answers_after: attempt|pass|never)
Question (id, quiz_id, text,
type: single|multiple|true_false|fill_blank,
options JSONB, correct_answers JSONB,
explanation TEXT, points INT)
QuizAttempt (id, quiz_id, user_id,
status: in_progress|submitted|graded,
started_at, submitted_at, score INT, passed BOOL,
question_order JSONB, answers JSONB)
Time Enforcement
Each attempt has a hard deadline: started_at + time_limit_seconds. The server auto-submits an in-progress attempt when the deadline passes — either via a background job or by checking expiry on the next request.
-- Background job
UPDATE quiz_attempts SET status = 'submitted', submitted_at = NOW()
WHERE status = 'in_progress'
AND started_at + INTERVAL '1 second' * (
SELECT time_limit_seconds FROM quizzes WHERE id = quiz_id
) < NOW();
Randomization
When an attempt is created, question IDs are shuffled (if randomize_questions) and option arrays are shuffled per question (if randomize_options). The resulting order is stored in question_order JSONB so grading always compares against the same arrangement.
Auto-Grading
-- For each objective question, compare submitted answer to correct_answers
score = SUM(q.points) WHERE submitted_answer = correct_answer
-- Mark attempt
passed = (score >= quiz.pass_score)
Fill-in-the-blank answers are normalized (trimmed, lowercased) before comparison. Open-ended questions are flagged for manual review.
Leaderboard
SELECT user_id, score,
EXTRACT(EPOCH FROM (submitted_at - started_at)) AS duration_seconds,
RANK() OVER (ORDER BY score DESC,
(submitted_at - started_at) ASC) AS rank
FROM quiz_attempts
WHERE quiz_id = ? AND status = 'graded'
ORDER BY rank;
At equal scores, faster completion ranks higher.
Anti-Cheat Measures
- Tab-switch detection: Page Visibility API fires an event when the browser tab loses focus. The client logs each event and sends it to the server.
- Suspicious event flags: Attempts accumulate a
flags JSONBcolumn recording event type, timestamp, and count. - Rate limiting: Enforce
max_attemptsper user per quiz, and optionally cap attempts per day via a counter in Redis. - IP tracking: Log IP per attempt for anomaly detection (multiple accounts from same IP).
See also: Atlassian Interview Guide
See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering