Stack Overflow
A Q&A site: anyone can ask, anyone can answer, and a number next to your name keeps score. The interesting part isn't the CRUD - questions and answers look almost identical on paper - it's noticing that they are almost identical, and building one shape for both instead of two.
Requirements
Functional
- A user posts a question with a title, a body, and a set of tags.
- Other users post answers to that question; the original asker can mark exactly one answer accepted.
- Users upvote or downvote a question or an answer; a user's vote can change but never stacks (one user, one live vote per post).
- Users comment on questions and answers.
- Questions can be looked up by tag.
Non-functional
- Looking up questions by tag must not scan every question in the system - the index should do the work, not a filter.
- The reputation formula is tuned constantly (an upvote is worth more some months than
others, an accepted answer even more) and that tuning should never touch
UserorQuestion.
Design
Question and Answer both carry votes and comments and both belong to a user - the only
real difference is that a question also has a title and tags. Duplicating vote-tally and
comment-list logic in two classes would mean every future bug gets fixed once and
re-introduced once, so both extend a shared Post that owns that behavior and nothing else.
- 1Asking and answering both go through the same front door.
- 2QaPlatform creates the post and indexes it by tag; it never hands the caller a raw map to mutate.
- 3A second user answers - same platform method family, a sibling post type.
- 4Answer and Question are siblings under Post, so this call looks identical from the platform side.
- 5Voting is a single method regardless of whether the target is a question or an answer.
- 6The post reports back a delta, not a raw new total - it doesn’t know what that delta is worth.
- 7The policy is the only class that turns a delta into reputation points.
- 8Only the asker can call this; it flips the question’s accepted-answer pointer and scores the answerer.
Reputation never gets touched directly by Post - every point awarded or docked runs
through a ReputationPolicy, so retuning the scoring is a one-file change.
Class diagram
Code
Design decisions
Postis an abstract base forQuestionandAnswer, not two independent classes. Voting, vote-changing, and commenting are identical for both; only the tagging and accepted-answer bookkeeping are question-specific. Pulling the shared half intoPostmeans a vote-tallying bug gets fixed in one place, not found twice.- A vote is a map entry keyed by user, not a growing list.
applyVotelooks up the caller's previous value, replaces it, and returns the delta. That's what makes "change your vote" and "vote once" the same code path instead of a special case bolted onto a running counter. - Reputation scoring lives entirely behind
ReputationPolicy.Post.applyVotereports a delta; it has no idea whether that delta is worth 5 points or 10, or whether an accepted answer is worth more than an upvote. That knowledge sits in exactly one place, so tuning the curve never means re-readingPostorQaPlatform. TagIndexis a plain map from tag to question set, updated on write.findByTagnever touches a question it doesn't need to - the cost of indexing is paid once, at ask-time, instead of on every lookup.- What's missing for a real system: duplicate-question detection (near-duplicate title
matching), comment threading beyond a flat list, and a moderation/close-vote workflow are
all real Stack Overflow features left out here because none of them change the shape
above - they'd each be a new class hanging off
PostorQuestion, not a rewrite of it.