•CASE STUDY

Content Moderation System (Harmful Post Detection)

7 min read·1,279 words·Advanced

Asked at

4 candidate reports between Jan 2026 and Feb 2026

How to use this case study

SDE-2 / Mid

  • Explain the pipeline
  • From new post to ML scoring to allow
  • Block or send to human review
  • How user reports feed in

SDE-3 / Senior

  • Go deeper on multimodal models (text + image + video)
  • Thresholds per policy
  • Sync vs async checks
  • The human review queue and appeals

Staff / Principal

  • Discuss precision/recall trade-offs per harm type
  • Adversarial users
  • Feedback loops for retraining
  • Latency budgets at upload
  • Measuring moderation quality (prevalence)

Problem RestatementProblem

Design a system that finds harmful content on a social platform. For example (asked at Meta): posts or ads selling weapons, as well as hate speech, nudity, spam and scams. Content can be text, images or video. The system checks new posts when they are uploaded. Clear violations are blocked, uncertain ones go to human reviewers, and users can report posts and appeal decisions.

The core tension: if we block too much, innocent people get hurt (false positives); if we block too little, harmful content spreads (false negatives).

RequirementsRequirements

1.1 Functional

  • Score every new post, comment, ad and profile for multiple policies (weapons, hate, nudity, spam, ...).
  • Take actions: allow, reduce reach, blur with a warning, remove, ban the account.
  • Human review queues with tools and priorities.
  • User reports and appeals.
  • Re-scan old content when policies or models change.

1.2 Non-Functional

  • Low latency at upload for high-risk checks (a few hundred ms), so bad content doesn't go live even briefly.
  • Scale: billions of items per day.
  • Accuracy tuned per policy (weapons sales: high recall; borderline humor: careful).
  • Auditability: why was this removed?

1.3 Scale Estimates

  • 2B new items/day ≈ 23K/sec, peaks of 100K/sec.
  • Images: ~40% of items. Video is fewer but much heavier (sample frames).
  • If 0.5% go to human review, that's 10M reviews/day, too many. So ML must handle most cases, and humans handle the uncertain middle.

1.4 API Design

POST/v1/moderate{ content_id, type, text, media_urls, author_id } → { decision: allow|hold|block, labels: [{ policy: "weapons_sale", score: 0.93 }] }
POST/v1/reports{ content_id, reason }
POST/v1/appeals{ content_id, message }

High-Level ArchitectureArchitecture

2.1 Overview

  • Fast inline checks (at upload): hash matching against known bad content (e.g., PhotoDNA-style perceptual hashes), blocked keywords, and a fast text and image classifier.
  • Async deep checks: heavier multimodal models (text + image + video frames + author signals), run within seconds to minutes after posting.
  • Decision engine: combines scores with per-policy thresholds and account history, then chooses an action.
  • Review system: queues by policy, language and severity, with a reviewer UI.
  • Feedback loop: reviewer decisions become training labels.

2.2 Architecture Diagram

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
    UP["New post / ad"] --> FAST["Fast checks: hash match, keywords, light model"]
    FAST -->|"clear violation"| BLK["Block"]
    FAST -->|"publish"| PUB["Post goes live"]
    FAST --> K[("Kafka - content events")]
    K --> DEEP["Deep multimodal models"]
    DEEP --> DEC["Decision engine - thresholds per policy"]
    RPT["User reports"] --> DEC
    DEC -->|"high score"| ACT["Remove / reduce reach"]
    DEC -->|"uncertain"| RQ[("Human review queues")]
    RQ --> REV["Reviewers"]
    REV --> ACT
    REV --> LBL[("Labels for retraining")]
    LBL --> TRAIN["Model training"]

Data ModelData model

content_scores:  content_id, model_version, policy, score, created_at
decisions:       content_id, action, policy, source (model|reviewer|appeal), actor, reason, created_at
review_tasks:    task_id, content_id, policy, priority, language, status, assigned_to, sla_due
known_hashes:    hash, policy, source   -- known violating images/videos

Key FlowsFlows

4.1 A new post with an image of a gun for sale

  1. Inline: check the perceptual hash against known bad images. No match. The text classifier sees "selling", "DM for price" and a weapon-related term, giving a weapons-sale score of 0.7, which is not high enough to block instantly.
  2. The post is published (or held briefly, for high-risk surfaces like ads) and an event is sent to Kafka.
  3. Deep model: an image model detects a firearm, the text is a sales intent, and the account is new with a pattern of similar posts. The combined score is 0.95.
  4. The decision engine applies the weapons-sale threshold (block at 0.9) → remove and notify the author with the reason and an appeal link.
  5. If the score were 0.6–0.9, it would go to the review queue instead, with priority based on predicted reach (viral posts first).

4.2 User report

Reports add a signal and can push content into review sooner. Many reports from trusted reporters raise priority.

Deep Dive A — What to do with a score of 0.7Deep dive

The classifier returns a number. Turning that number into an action is where a moderation system is actually designed, and where it most often goes wrong.

Weak

One threshold, remove above it

Pick 0.8. Anything scoring higher is deleted, anything lower is published.

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  S["Score 0.79 - weapons sale"] --> PUB["Published - real harm"]
  S2["Score 0.81 - satire about weapons"] --> DEL["Deleted - wrongful removal"]
  DEL --> APP["Appeal, press coverage, reinstated a week later"]

One number has to serve every policy at once, and the policies want opposite things. Spam and nudity tolerate false positives; political speech and satire do not. A single threshold guarantees you are too aggressive somewhere and too lax somewhere else, and there is no setting that fixes both.

Good

A threshold per policy

Tune each policy separately against labelled data. Weapons sales lean towards recall — catch nearly all of them, accept extra false positives. Satire and commentary lean towards precision — only act when confident, accept that some violations get through.

Much better, and still binary. Every piece of content is either published untouched or destroyed, so the whole uncertain middle — which is most of the volume — has to be forced to one side. Setting the weapons threshold for high recall now deletes a lot of legitimate posts, and there is nothing softer to do with them.

Best

Graded actions, and measure the outcome rather than the model

Give the score a range of responses instead of a switch:

Architecture diagram
%%{init: {"look":"handDrawn","handDrawnSeed":7,"theme":"base","fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","themeVariables":{"fontFamily":"Virgil, \"Segoe Print\", \"Comic Sans MS\", cursive","fontSize":"16px","primaryColor":"#fff4e6","primaryBorderColor":"#1e1e1e","primaryTextColor":"#1e1e1e","secondaryColor":"#e7f5ff","tertiaryColor":"#ebfbee","lineColor":"#1e1e1e","textColor":"#1e1e1e","mainBkg":"#fff4e6","nodeBorder":"#1e1e1e","clusterBkg":"#f8f9fa","edgeLabelBackground":"#ffffff","classText":"#1e1e1e"}}}%%
flowchart LR
  SC["Policy score"] --> H{"Very high"}
  H -->|"yes"| RM["Remove and notify - clear violation"]
  H -->|"no"| M{"Moderate"}
  M -->|"yes"| SOFT["Reduce reach, add a warning interstitial"]
  M -->|"no"| L{"Uncertain, high reach"}
  L -->|"yes"| Q["Human review queue - ranked by views at risk"]
  L -->|"no"| PUB["Publish"]
  SOFT --> Q

Reducing reach is the option the binary design cannot express, and it carries most of the value: a borderline post that reaches fifty people instead of fifty thousand has had its harm cut by three orders of magnitude without anyone's speech being deleted. The review queue is ranked by views at risk, not by arrival time, because reviewer capacity is fixed and a post with two views can wait.

Then measure the system, not the classifier. Prevalence — sample content randomly every day, have experts label it, and estimate what share of views were of violating content — is the number that tells you whether moderation works. Model precision and recall are inputs to that; they are not the goal, and they can both improve while prevalence gets worse.

Deep Dive B — Adversaries and scaleScale

  • Evasion: people misspell words, put text in images, crop or recolor images. Countermeasures: OCR on images, perceptual hashes that survive small edits, embeddings that capture meaning, and account-level signals (new account, many similar posts, links to known bad groups).
  • Video: sample frames (e.g., 1 per second plus scene changes) and audio transcripts, and scan the most-viewed videos more deeply.
  • Model updates: when a new model or policy ships, re-scan recent content in the background, starting with the most-viewed.
  • Reviewer wellbeing and quality: blur by default, limit exposure time, and double-review a sample to measure reviewer accuracy.

Trade-offs & AlternativesTrade-offs

DecisionChoiceWhyAlternative
When to checkFast inline + deep asyncLow latency, deep coverageEverything inline: slow uploads
DecisionsPer-policy thresholds + graded actionsFits different harmsOne global threshold: poor fit
HumansReview only the uncertain middleScales, keeps qualityReview everything: impossible
Known contentPerceptual hash matchingInstant, precise for re-uploadsModel only: misses exact known items less reliably

Common Follow-up QuestionsFollow-ups

  • "Ads vs posts?" Ads are paid and higher risk, so review them before they go live. Posts go live and are checked right after, unless they're high-risk.
  • "Multiple languages?" Multilingual models, plus review queues routed by language.
  • "How do appeals work?" A different reviewer re-checks the item. If overturned, restore the content and add the case as a training label.

Wrap-UpWrap-up

Run fast checks at upload (hash matching, keywords, a light model) to stop obvious violations, then deep multimodal models asynchronously. A decision engine applies per-policy thresholds to allow, limit, remove or send to human review, prioritized by reach. Feed reviewer decisions back into training, fight evasion with OCR, perceptual hashes and account signals, and measure success with sampled prevalence.

More Case Studies

Frequently Asked Questions

What is the Content Moderation System (Harmful Post Detection) system design question?

Content Moderation System (Harmful Post Detection) is a system design interview question asked at FAANG companies. It covers ai / ml, security, data pipelines, real-time and tests your ability to design scalable, production-ready systems. InterviewSkool's breakdown walks you through requirements, API design, architecture, and trade-offs.

Which companies ask the Content Moderation System (Harmful Post Detection) question?

Meta, TikTok have reportedly asked variations of this question in system design interviews. The exact wording may differ, but the core design challenges remain the same.

How should I prepare for the Content Moderation System (Harmful Post Detection) interview question?

Start with the problem statement and scale estimates, then design the high-level architecture. Focus on the core components, data model, and API design. InterviewSkool's breakdown covers the full solution with mermaid diagrams and trade-off analysis to help you prep efficiently.

What level is the Content Moderation System (Harmful Post Detection) question?

This question is suitable for SDE-2, SDE-3, and Staff engineer interviews. The level guidance on this page provides specific tips for each level — SDE-2 candidates should focus on core architecture, while Staff engineers should discuss trade-offs, monitoring, and incremental rollouts.

Practice with a Mock Interview

Apply what you learned in a live system design mock interview with InterviewSkool's AI interviewer.

Start System Design Interview →