100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

How to Design a Polling System?

Learn how to design a scalable live polling system: idempotent voting, incremental tallies, and real-time WebSocket result fan-out.

mediumQ95 of 224 in System Design Est. time: 5 minsLast updated:
Open Code Lab

Expected Interview Answer

A polling system (like Slido or Mentimeter) is designed around a write-heavy vote-ingestion path decoupled from a read-heavy results path, using an append-only vote log, idempotency keys to prevent double voting, and a cache-backed or streaming aggregation layer so results update in near real time without recomputing from scratch on every request.

The core write path accepts a vote tied to a poll ID, an option ID, and a voter identity token, checks a fast idempotency store (Redis SETNX or a unique constraint) to reject duplicates, then appends the vote to a durable log or database row and increments a counter asynchronously. The read path never scans raw votes for a live poll; instead it reads a materialized tally that is updated incrementally, either via a cache counter (INCR in Redis) refreshed on every write or via a stream processor (Kafka + a consumer) that folds votes into aggregate state. Fan-out to viewers uses WebSockets or Server-Sent Events with the poll ID as a topic, pushing incremental tally deltas instead of full result payloads. At scale, poll creation and voting are sharded by poll ID so a single viral poll does not bottleneck the whole cluster, and closed polls are archived to cold storage with the final tally frozen.

  • Idempotency keys prevent one participant from skewing results with duplicate votes
  • Incremental tally updates avoid expensive full re-aggregation on every vote or view
  • WebSocket/SSE fan-out delivers near real-time results without client polling storms
  • Sharding by poll ID isolates a viral poll from degrading unrelated polls

AI Mentor Explanation

A polling system is like a stadium scoreboard operator tallying a live fan vote for player of the match. Each text-in vote is checked against a phone-number list so no one votes twice (idempotency), then added to a running tally the operator keeps updating rather than recounting every message from scratch. The big screen refreshes with the new percentage seconds later, not by replaying every vote, just by adding the delta. That incremental tally-and-broadcast loop, with duplicate checking at the door, is exactly how a polling system scales.

Step-by-Step Explanation

  1. Step 1

    Accept and dedupe the vote

    A vote request carries a poll ID, option ID, and voter token; an idempotency check (Redis SETNX or unique DB constraint) rejects duplicates before anything is persisted.

  2. Step 2

    Persist and increment

    The vote is appended to a durable log/table and a cached counter for that option is atomically incremented, avoiding a full recount.

  3. Step 3

    Fan out incremental updates

    A WebSocket/SSE layer pushes just the tally delta to all viewers subscribed to that poll ID instead of a full payload.

  4. Step 4

    Shard and archive

    High-traffic polls are sharded by poll ID to isolate load, and closed polls are frozen and archived to cold storage.

What Interviewer Expects

  • Identifies the write path (vote ingestion) and read path (live tally) as separately scaled concerns
  • Names a concrete idempotency mechanism to prevent duplicate votes
  • Describes incremental aggregation instead of recomputing totals from raw votes
  • Mentions real-time fan-out via WebSockets/SSE and sharding by poll ID for hot polls

Common Mistakes

  • Recomputing the full tally from raw vote rows on every page view
  • Forgetting to prevent duplicate votes from the same participant
  • Using client-side polling (HTTP requests every few seconds) instead of push-based updates at scale
  • Not considering what happens when a single poll goes viral and receives disproportionate traffic

Best Answer (HR Friendly)

โ€œA polling system needs to handle two very different jobs well: quickly recording votes without letting anyone vote twice, and showing everyone an up-to-date result without recalculating everything from scratch each time. I would use a fast duplicate check when a vote comes in, keep a running total that just gets incremented, and push live updates to viewers instead of making them keep refreshing.โ€

Code Example

Vote ingestion with idempotency and incremental tally
async function castVote(pollId, optionId, voterToken) {
  const idempotencyKey = `voted:${pollId}:${voterToken}`
  const isFirstVote = await redis.set(idempotencyKey, "1", "NX", "EX", 86400)

  if (!isFirstVote) {
    return { accepted: false, reason: "duplicate_vote" }
  }

  await db.votes.insert({ pollId, optionId, voterToken, castAt: Date.now() })

  const newCount = await redis.incr(`tally:${pollId}:${optionId}`)

  await pubsub.publish(`poll:${pollId}`, {
    optionId,
    count: newCount,
  })

  return { accepted: true, newCount }
}

Follow-up Questions

  • How would you prevent a bot farm from stuffing the ballot on an anonymous public poll?
  • How would you design results fan-out for a poll with one million concurrent viewers?
  • What happens to in-flight votes if the poll closes while requests are still arriving?
  • How would you shard poll storage so one viral poll does not degrade other polls?

MCQ Practice

1. Why should a polling system avoid recomputing the full tally from raw vote rows on every request?

Recomputing from scratch scales poorly; incremental counters keep read latency constant regardless of total votes cast.

2. What is the primary purpose of an idempotency key in a polling system?

The idempotency key (based on voter identity) ensures retries or duplicate submissions are not double-counted.

3. What is the most scalable way to deliver live poll results to thousands of concurrent viewers?

Push-based delivery of small deltas avoids the request storm and redundant payloads that repeated HTTP polling causes.

Flash Cards

Why separate the vote write path from the results read path? โ€” Because they have very different load and latency characteristics; separating them lets each scale independently.

How do you stop duplicate votes? โ€” An idempotency key tied to voter identity, checked before the vote is persisted (e.g., Redis SETNX or a unique DB constraint).

How are live tallies kept fast? โ€” By incrementing a cached counter on each vote instead of recomputing totals from raw vote rows.

How are results pushed to viewers? โ€” Via WebSockets or Server-Sent Events, sending small incremental deltas rather than full payloads.

1 / 4

Continue Learning