Design APIs for a Voting System

Operations

Method Endpoint Purpose
POST /v1/polls Create a poll
GET /v1/polls/{id} Get poll details
PATCH /v1/polls/{id} Update a poll (e.g., close, edit title, extend expiration)
GET /v1/polls List polls
GET /v1/polls/{id}/results Get poll results
DELETE /v1/polls/{id} Deleta a poll
POST /v1/polls/{id}/votes Cast a vote
PUT /v1/polls/{id}/votes/me Change your vote
DELETE /v1/polls/{id}/votes/me Remove your vote
GET /v1/polls/{id}/votes/me Retrieve your vote

Database Schema

polls
-----
id PK
creator_id
title
description
status
expires_at
created_at


poll_options
-------------
id PK
poll_id FK
text


votes
-----
id PK
poll_id FK
option_id FK
user_id
created_at
updated_at

UNIQUE(poll_id,user_id)


vote_counts (optional for scale)
------------
poll_id
option_id
vote_count

Difference Between Operations

  • PUT → Replace the entire resource.
  • PATCH → Update one or more specific fields of an existing resource.
  • POST → Create a resource or invoke a domain-specific action. It is generally used for creating resources or triggering actions.

For interview API design, PATCH for simple state changes like ACTIVE → CLOSED is typically the cleanest RESTful choice, while POST /close is a good alternative when "closing" has significant side effects beyond updating a field.

For DELETE, one thing to consider in a real-world system is whether polls are hard deleted or soft deleted. Many production systems use a field like status = DELETED or deleted_at instead of physically removing the record.

Optimistic UI Update

When a user clicks "Vote Python", the frontend optimistically updates the UI and immediately displays Python with 101 votes (100 + 1) without waiting for the server response. Meanwhile, the frontend sends POST /v1/polls/123/votes to the backend. If the backend successfully validates and persists the vote, it returns success, and the UI keeps the optimistic value. The key point is: "The frontend does not know yet whether the vote succeeded. It is making a temporary assumption."

if the vote fails, like user already voted, poll closed, network error, server error, then the frontend rolls back from 101 to 100, and shows the error message. This is called optimistic rollback.

Multiple Users Vote Together

When Alice and Bob vote at the same time, each user's frontend immediately increments the count locally by 1 without waiting for the backend response. If the initial count is 100, both Alice and Bob may temporarily see 101 on their own screens. However, the backend stores both votes, so the actual count becomes 102. Later, when the frontend refreshes the results or receives an update, both users' UIs are corrected to show 102.

Trade-off: Fast user experience but temporarily stale/inaccurate counts. This is acceptable for low-stakes voting systems such as social polls.

Summarize: For a normal poll, I would use optimistic UI updates. Alice may temporarily see 101 even though the true count is 102 because another user can vote concurrently. The backend remains the source of truth. After the vote succeeds, we can refresh the result or receive a real-time update through WebSocket to reconcile the count. For applications requiring strict accuracy, we would avoid optimistic counts and return a strongly consistent counter from the backend.

posted @ 2026-08-03 15:11  YBgnAW  阅读(1)  评论(0)    收藏  举报