DrawLintDrawLint.ai

Dating App / Tinder — system design by AgileViper46

Lean Hire

Reviewed by 6 specialized AI reviewers. Explore the diagram and the full per-section feedback below.

Loading diagram…

Hire SignalLean Hire

Overall this is a credible mid-level design with correct core modeling, reasonable scale estimation, and several good architectural instincts. The candidate demonstrates solid understanding, but the missing API coverage for explicit requirements and the unclear feed/storage design keep it short of a stronger hire signal.

✅ Good

Latency target is explicitly defined

The design states a concrete P95 latency target for fetching potential matches (<200ms), which is the right kind of measurable NFR for this product and gives a clear bar for evaluating whether the system meets user experience expectations.

✅ Good

Consistency model is appropriately scoped

Calling out eventual consistency for profile updates while requiring read-your-own-write behavior for the updating user is a sensible tradeoff for this use case. It balances scalability with a user experience requirement that would otherwise be confusing if users could not immediately see their own changes.

✅ Good

Write throughput assumption is quantified

The design translates the DAU assumption into an estimated swipe rate (~23K/sec average), which is useful for sizing and for checking whether the system can handle the expected write load instead of leaving scalability as a vague statement.

warning

Availability target is missing

Basic NFR coverage should include an explicit availability goal, especially for a consumer app at 20M DAU. Without a target such as 99.9% or 99.99% for core flows like swiping and match delivery, it is hard to judge whether the design is sufficient. Add clear uptime/SLA expectations for the main user actions.

warning

Real-time match requirement is not measurable

The design says match updates should be real time, but does not define what that means operationally. Specify a target such as match notification delivery within a few seconds at P95/P99 so the system can be evaluated against a concrete latency objective.

info

Average swipe rate alone may understate capacity needs

The stated 23K swipes/sec is an average derived from DAU, but large consumer apps usually see strong diurnal peaks. It would be better to also state an expected peak multiplier or peak QPS target so the scalability requirement is complete and not accidentally under-provisioned.

✅ Good

Core matching nouns are present

The design identifies the main domain entities needed for the stated requirements: Profile for user preferences and distance settings, Swipe for yes/no decisions, and Match for mutual positive swipes. These are the key nouns needed to model the basic dating flow.

✅ Good

Core traffic estimates are present

The section includes the key baseline numbers expected at this level: DAU, average user actions per day, derived total daily operations, and rough read/write QPS. That gives the rest of the design a usable capacity anchor instead of staying purely qualitative.

✅ Good

Storage sizing is methodical

The candidate converts swipe volume into daily and yearly storage using an explicit per-record size assumption, and also separately sizes profile storage. Even if the exact byte assumptions may vary, the approach is correct and in the right form for capacity planning.

✅ Good

Numbers are in the right ballpark for the stated assumptions

For 20M DAU and 100 swipes per user per day, arriving at roughly 23K average write QPS and tens of thousands of read QPS is a reasonable order-of-magnitude estimate. This is appropriate for a mid-level interview and shows the candidate understands how to translate product usage into system load.

warning

Only average QPS is estimated, not peak traffic

Using daily volume divided by 86,400 gives average load, but a consumer app with 20M DAU will have strong diurnal peaks. Sizing only for 23K write QPS and 45K read QPS risks underprovisioning by several times during busy hours. Add a peak multiplier assumption (for example 3x-5x average) and compute peak read/write QPS explicitly.

info

Read estimate is too lightly justified

The statement that each swipe needs about 2 reads is plausible, but it is not broken down. For example, candidate retrieval, profile fetch, prior-swipe filtering, and mutual-like check may each contribute reads unless cached or precomputed. A short explanation of what those reads represent would make the estimate more defensible.

info

Match-rate calculation should clarify whether it is event rate or stored data rate

The estimate of about 23 matches/sec is directionally fine, but it would be stronger to state whether this is used for notification throughput, match-record writes, or both. Tying the number to a concrete capacity implication would make the calculation more useful.

✅ Good

Core swipe flow is represented

The routes cover the main user actions in the requirements: fetching a candidate stack, setting matching preferences, and submitting a swipe decision. This gives a workable end-to-end API for the basic matching flow.

✅ Good

Simple action-oriented swipe endpoint

Using a dedicated POST endpoint for swipes is a reasonable choice because a swipe is an event/action rather than a pure resource replacement. Including the decision in the body keeps the API straightforward for clients.

critical

Missing profile creation/update API

One of the functional requirements is that users can create a profile with preferences, but the routes only include preferences and not profile creation or update. Add profile endpoints such as POST /profiles or POST /me/profile and PATCH /me/profile so clients can create and maintain the user profile needed for matching.

critical

No API/message path for match notification

The requirements explicitly say users get a match notification after a mutual swipe, but there is no endpoint or real-time message design for delivering that notification. Add either a push/notification API contract or a WebSocket/SSE event such as match_created with the matched user payload so the client can receive the match result.

warning

Primary entities do not have clear CRUD coverage

For the main entities exposed here, only partial operations are defined. Preferences can be set but not retrieved or updated explicitly, and profiles are missing entirely. A more complete API would include GET /me/preferences and PATCH /me/preferences, plus profile create/read/update routes.

warning

Feed route mixes client-supplied distance with stored preferences

The requirements say users specify a maximum distance as part of their preferences, but the feed endpoint also accepts distance as a query parameter. This creates ambiguity about which value is authoritative and can let clients bypass saved preferences. Prefer GET /feed?lat=...&long=... and use the stored maxDistance server-side, or clearly define override behavior.

info

Use a user-scoped REST shape for preferences

POST /preferences works, but a more conventional structure would scope preferences to the authenticated user, for example GET /me/preferences and PATCH /me/preferences. This makes ownership clearer and better aligns with REST resource modeling.

✅ Good

Reasonable service separation by domain

Splitting the system into profile, feed, swipe, and notification services is a solid high-level architecture choice. It keeps the main functional flows isolated and makes the end-to-end design easier to scale independently for read-heavy feed traffic versus write-heavy swipe traffic.

✅ Good

Asynchronous match notification path

Using Redis Streams between swipe processing and notification delivery is a good pattern for decoupling user actions from notification fanout. This improves resilience and helps absorb spikes in swipe volume without blocking the swipe API.

✅ Good

Search index for feed filtering

Introducing Elasticsearch for feed retrieval is a sensible choice for preference-based discovery, especially at the stated 20M DAU scale. It is better suited than a primary relational database for filtering candidate profiles by attributes and location-related queries.

critical

Feed query path is inconsistent and unlikely to scale

The feed service is shown querying Postgres to 'show users within max_distance not in match and swipe table', while Elasticsearch is also present for search. Doing distance filtering plus exclusion against swipe/match history directly in Postgres at 20M DAU will become a bottleneck, and the architecture does not clearly define which store is the source for candidate generation. A better design is to use Elasticsearch (or another geo-capable index) for candidate retrieval and use a dedicated exclusion store/cache for already-swiped or matched users.

warning

Duplicate components are not explained as replicas or shards

Profile service, feed service, swipe service, and notification servers each appear twice, but there is no annotation explaining whether these are replicas for HA, horizontal scaling, or accidental duplication. At HLD level this creates ambiguity in the deployment model. Label them clearly as stateless replicas behind the gateway/load balancer or show partitioning strategy if they are sharded.

warning

Notification presence model is incomplete

The design uses POST /online and SSE notification servers, but there is no clear presence store or connection/session management component to track which users are currently connected to which notification server. Without that, routing real-time match notifications reliably is difficult. Add a lightweight presence registry in Redis or similar so notification workers can determine whether to deliver via SSE or fall back to push.

warning

Several components are weakly integrated or orphan-like

The generic 'cache' populated by Cron and read by Feed service is too vague, and Cron -> Elasticsearch is also unclear. These components do not have a well-defined role in the main functional flows, which makes the architecture harder to reason about operationally. Either specify exactly what is being precomputed and why, or remove these boxes to keep the design focused and avoid orphaned infrastructure.

warning

Swipe and match storage boundaries are unclear

Swipe service writes to Cassandra and Postgres, but the ownership of swipe records versus match records is not clearly separated. This can lead to consistency and operational complexity if both databases participate in the same user action. A cleaner HLD would define Cassandra as the high-write swipe store and Postgres as the durable match/profile store, with an explicit async or transactional strategy for match creation.

info

Cache strategy needs clearer purpose

The design includes both 'Swipe cache' and another generic 'cache', but their contents, eviction policy, and role in reducing database load are not described. Clarifying whether Swipe cache stores prior swipe exclusions, bloom filters, or hot candidate sets would make the scaling story stronger.

Want this kind of feedback on your own design?

Draw your architecture for Dating App / Tinder and get an instant hire/no-hire signal from 6 specialized AI reviewers — free to start.