tezvyn:

Design a referral system: data models, APIs, attribution, self-referral prevention

AI-drafted, machine-checkedSource: dev.tointermediate
Design a referral system: data models, APIs, attribution, self-referral prevention

Tests data modeling with fraud guardrails and idempotent rewards. Cover: Users with nullable referred_by, ReferralEvents state table, async ledger attribution, and device-fingerprint self-referral blocks. Red flag: bare integer credit with no audit trail.

WHAT THIS TESTS: This question tests your ability to model a one-to-many referral relationship with transactional integrity and fraud guardrails. Interviewers care about idempotency, auditability, and the boundary between signup and reward. They want to see that you understand race conditions on concurrent redemptions, double-spending of referral credits, and the difference between application-level checks and database-level constraints.

A GOOD ANSWER COVERS: First, schema design. A Users table should have a unique indexed referral_code and a nullable referred_by foreign key. A separate ReferralEvents table captures referral_code_used, invitee_user_id, device_fingerprint, ip_address, and a status enum such as pending, completed, or failed. This decouples account creation from reward payout. Second, API design. Expose a POST endpoint to generate or retrieve a unique code, a POST signup endpoint that accepts an optional referral_code and writes the pending event, and a POST complete-action or webhook endpoint that triggers reward fulfillment. Third, reward attribution. Use an async job or outbox pattern to move events from pending to completed. Write reward distribution as ledger entries with idempotency keys derived from the ReferralEvents row UUID so duplicate events do not double-credit. Fourth, self-referral prevention. Check the new user's email domain, phone hash, device fingerprint, or IP against the referrer's record. Enforce this in the application layer and, where possible, with a database check constraint or a unique partial index that blocks matching fingerprints.

COMMON WRONG ANSWERS: Storing referral credit as a simple integer column on the Users table without an audit ledger. This loses traceability and creates race conditions when two invitees sign up simultaneously. Checking self-referral only by comparing raw email strings and ignoring device or IP fingerprinting. Making reward distribution synchronous inside the signup transaction, which adds latency and can roll back the account creation if the reward service fails. Using a boolean reward_granted flag instead of a state machine, which makes retries and observability difficult.

LIKELY FOLLOW-UPS: How would you prevent brute-force enumeration of referral codes? How do you cap total rewards per referrer or expire codes after a time window? What happens if a user deletes their account after rewards were already paid out? How would you extend this to multi-level or tiered referrals?

ONE CONCRETE EXAMPLE: Imagine a payment app where the referrer earns 201 and the invitee earns 51 after the invitee completes a first transaction. The signup API accepts a referral_code and creates a ReferralEvents row with status pending and stores a device fingerprint. A background job listens for the first-transaction event, verifies the invitee fingerprint does not match the referrer fingerprint, then inserts two ledger rows with idempotency keys based on the referral event UUID. It updates the event status to completed and sends notifications to both users. If the job retries, the ledger insert is idempotent and the reward is not duplicated.

Source: DEV Community, System Design of a Referral System

Read the original → dev.to

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.