How to Design a Survey Platform?
Learn how to design a survey platform: flexible schema-per-survey modeling, schema versioning, and scalable response analytics.
Expected Interview Answer
A survey platform (like Google Forms or Typeform) is designed around a flexible schema-per-survey data model that stores question definitions separately from responses, uses a versioned form schema so edits do not corrupt historical answers, and separates the write-heavy response-submission path from the read-heavy analytics/reporting path.
Survey definitions are stored as a JSON-like schema (question types, options, validation rules, branching logic) rather than rigid relational columns, since every survey has a different shape; a form-builder document model or an EAV (entity-attribute-value) pattern works well here. Each submitted response is stored as its own record referencing the survey schema version at submission time, so later edits to the survey (adding/removing a question) never retroactively corrupt older responses. Response ingestion is optimized for high write throughput — validate against the schema, persist the raw response, then enqueue it for asynchronous aggregation into denormalized analytics tables or a data warehouse. Branching/conditional logic (skip patterns) is evaluated client-side for responsiveness but re-validated server-side on submission to prevent tampering. At scale, response storage is partitioned by survey ID, and analytics queries run against precomputed aggregates rather than scanning raw responses.
- Schema-per-survey flexibility supports arbitrary question types without a rigid relational model
- Schema versioning keeps historical responses valid even after the survey is edited
- Separating ingestion from analytics lets each be scaled and optimized independently
- Server-side re-validation of branching logic prevents tampered or inconsistent submissions
AI Mentor Explanation
A survey platform is like a cricket academy’s player evaluation form that changes year to year — sometimes adding a new fielding drill, sometimes dropping an old fitness test. Each evaluation sheet a coach fills out is tagged with the exact version of the form used that year, so last year’s scores are never misread against this year’s questions. New evaluations flow into a fast intake pile, and separately, someone tallies trends across seasons using a summarized report rather than re-reading every sheet ever filled. That versioned form plus separate fast intake and slow summarizing is exactly how a survey platform is architected.
Step-by-Step Explanation
Step 1
Model the survey as a flexible schema
Store question definitions (type, options, branching rules) as a document/JSON schema rather than rigid relational columns, since surveys vary wildly in shape.
Step 2
Version the schema on every edit
Each response references the exact schema version live at submission time, so later edits never corrupt or misinterpret historical answers.
Step 3
Ingest responses on the write-optimized path
Validate against the schema, re-check branching logic server-side, persist the raw response, then enqueue for async aggregation.
Step 4
Serve analytics from precomputed aggregates
Dashboards and reports read from denormalized rollups or a warehouse, never scanning raw response tables live.
What Interviewer Expects
- Recognizes surveys need a flexible, schema-per-form data model rather than fixed columns
- Explains schema versioning so editing a live survey does not corrupt historical responses
- Separates fast response ingestion from slower analytics/reporting
- Mentions server-side re-validation of client-evaluated branching/skip logic
Common Mistakes
- Using a single rigid relational table for all survey questions regardless of type
- Forgetting that editing a survey can retroactively break interpretation of old responses without versioning
- Trusting client-side branching logic without re-validating it server-side
- Computing analytics by scanning raw response rows on every dashboard load
Best Answer (HR Friendly)
“A survey platform has to handle very different question types and forms that change over time, so I would store each survey as a flexible schema rather than fixed database columns, and tag every response with the exact version of the survey it was answered under. That way editing a survey later never breaks how old answers are interpreted, and I would keep response collection fast and separate from the slower job of building reports.”
Code Example
async function submitResponse(surveyId, schemaVersion, answers) {
const schema = await surveySchemas.get(surveyId, schemaVersion)
if (!schema) {
throw new Error("survey_version_not_found")
}
for (const question of schema.questions) {
if (question.required && answers[question.id] === undefined) {
throw new Error(`missing_required_answer:${question.id}`)
}
if (question.showIf && !evaluateBranch(question.showIf, answers)) {
delete answers[question.id] // ignore answers to skipped branches
}
}
await responses.insert({
surveyId,
schemaVersion,
answers,
submittedAt: Date.now(),
})
await analyticsQueue.enqueue({ surveyId, schemaVersion, answers })
}Follow-up Questions
- How would you support skip logic (conditional questions) without letting users bypass it via the API directly?
- How would you migrate analytics when a survey question type changes mid-campaign?
- How would you support exporting millions of responses to CSV without overloading the database?
- How would you detect and filter low-quality or bot-generated survey responses?
MCQ Practice
1. Why do survey platforms typically store survey definitions as a flexible schema rather than fixed relational columns?
Since every survey can have a different set and type of questions, a flexible schema (JSON/document model) accommodates that variability far better than fixed columns.
2. Why is schema versioning important when a survey is edited after responses have already been collected?
Without versioning, editing a live survey (adding/removing questions) can make historical responses ambiguous or incorrectly mapped to new questions.
3. Why should branching/skip logic be re-validated on the server even if it was already evaluated client-side?
Client-side logic can be bypassed by a modified client, so the server must independently enforce the same branching rules to keep data valid.
Flash Cards
Why use a flexible schema for surveys? — Because each survey can have a completely different set and type of questions, which does not fit rigid relational columns.
Why version the survey schema? — So editing a live survey does not retroactively corrupt or misinterpret previously collected responses.
Why separate ingestion from analytics? — Response submission is write-heavy and latency-sensitive; analytics is read-heavy and can be computed asynchronously from aggregates.
Why re-validate branching logic server-side? — To prevent a tampered client from submitting invalid answers to questions that should have been skipped.