Summary
Lyft system design round focused entirely on building a news feed backend. It was one of the more thorough design interviews I've done, covering basically every layer of the stack from data modeling to failure handling.
Questions Asked(7)
I started with requirements which felt fine, but I spent too long on the functional side and had to rush the non-functional stuff.
Suggested Approach
Start by clarifying requirements and scale assumptions before diving into design, as this demonstrates engineering maturity and prevents wasted effort on the wrong problem. Structure your answer by progressively layering complexity: data model first, then APIs, then the architecture that ties them together. Explicitly call out trade-offs at each decision point to show you understand there is no single correct answer.
Clarify Requirements & Constraints
Ask about scale (DAU, posts per second, feed size), feature scope (likes, comments, ranking vs. chronological), and consistency requirements (eventual vs. strong). Establish whether the feed is follower-based, interest-based, or both.
Define the Data Model
Design core entities: User, Post, Follow/Edge, and FeedItem tables. Discuss storage choices — relational DB (PostgreSQL) for user/follow graphs, a NoSQL store (Cassandra or DynamoDB) for high-write post and feed data, and a cache layer (Redis) for hot feed timelines.
Design the APIs
Define RESTful endpoints such as POST /posts to create content, GET /feed?user_id=&cursor= for paginated feed retrieval, and POST /follow to manage relationships. Emphasize cursor-based pagination over offset for performance at scale.
Architect the Feed Generation Pipeline
Explain the fanout strategy: fanout-on-write (pre-compute feeds into a Redis timeline per user) for most users, but fanout-on-read for high-follower celebrities to avoid write amplification. Use an async message queue (Kafka) to decouple post creation from feed distribution.
Address Scalability, Reliability & Ranking
Discuss horizontal scaling of feed services, CDN for media assets, and a ranking/ML layer to re-order feed items by relevance. Mention monitoring, rate limiting, and graceful degradation (e.g., serving a stale cached feed if the ranking service is down).
Key Points to Mention
This is where things got interesting.
Suggested Approach
Start by clearly defining both approaches and their core trade-offs, then anchor your recommendation to the specific constraints given — high write AND read loads — which makes this a nuanced hybrid scenario rather than a clear-cut choice. Demonstrate that you understand neither approach is universally superior and that real-world systems like Twitter, Instagram, and Lyft's own feed often use a blended strategy.
Define Both Approaches
Briefly explain fan-out-on-write (pre-computing and pushing posts to followers' feed caches at write time) versus fan-out-on-read (aggregating a user's feed dynamically at read time from followed accounts). Establish a shared vocabulary before diving into trade-offs.
Analyze Write-Heavy Load Impact
Discuss how fan-out-on-write amplifies write cost — a single post by a user with 1M followers triggers 1M cache writes — making it expensive under high write loads. Contrast this with fan-out-on-read, which defers work to read time but keeps writes cheap.
Analyze Read-Heavy Load Impact
Explain that fan-out-on-read under high read load causes repeated, expensive aggregation queries (joining across many followed accounts) that can overwhelm the database. Fan-out-on-write shines here because reads are O(1) cache lookups, but only if the cache is kept warm and consistent.
Propose a Hybrid Strategy
Recommend a tiered hybrid: use fan-out-on-write for the majority of users (low follower counts) to ensure fast reads, but fall back to fan-out-on-read for 'celebrity' or high-follower accounts to avoid write amplification storms. Mention that the feed is assembled by merging the pre-computed cache with a real-time read for celebrity posts.
Address Supporting Infrastructure
Briefly mention the infrastructure needed to make the hybrid work — a message queue (e.g., Kafka) to handle async fan-out workers, a cache layer (e.g., Redis) for pre-computed feeds, and a follower-count threshold to dynamically route between strategies.
Key Points to Mention
Blanked briefly on real-time.
Suggested Approach
Start by clarifying the feed's purpose and scale requirements (e.g., driver availability feed, ride activity feed), then architect a solution that separates ranking logic from real-time delivery. Walk through the trade-offs between pre-computed rankings and on-the-fly computation, grounding your answer in Lyft's specific domain of high-frequency location and status updates.
Clarify Requirements & Scale
Ask about the feed type (driver feed, activity feed, notifications), expected QPS, number of active users/entities, and acceptable latency for updates. Establish whether 'real-time' means sub-second or near-real-time (a few seconds).
Define the Ranking Model
Describe the ranking signals relevant to Lyft's context — proximity, driver rating, ETA, surge pricing, ride type eligibility — and decide whether ranking is rule-based or ML-driven. Clarify whether ranking is computed per-user or globally.
Design the Data Pipeline
Propose a streaming pipeline (e.g., Kafka for ingesting real-time events like location pings or status changes) feeding into a processing layer (Flink or Spark Streaming) that recomputes rankings incrementally. Explain how pre-computed results are stored in a low-latency store like Redis or Cassandra.
Handle Real-Time Delivery
Discuss push vs. pull mechanisms — WebSockets or Server-Sent Events for pushing ranked feed updates to clients, with a polling fallback. Introduce a fan-out strategy and explain how to avoid thundering herd problems during high-demand periods like surge events.
Address Trade-offs & Failure Modes
Cover consistency vs. availability trade-offs (e.g., serving a slightly stale ranking during a ranking service outage), cache invalidation strategies, and how to gracefully degrade — such as falling back to distance-only ranking if the ML scoring service is down.
Key Points to Mention
Privacy and blocks I handled fine since it's mostly a filter layer at read time.
Suggested Approach
Frame your answer around the feed pipeline as a multi-stage system where each concern (deduplication, privacy, blocks) is enforced at the most efficient layer — ideally as early as possible to avoid wasted computation. Walk through the data flow from content ingestion to final rendering, explaining where and why each control is applied. Emphasize the trade-offs between correctness, latency, and scalability at each stage.
Define the Pipeline Stages
Start by outlining the feed pipeline stages: content ingestion, candidate generation, ranking, filtering, and delivery. This establishes a shared mental model and shows you think in systems.
Address Deduplication
Explain how deduplication is handled using content fingerprinting (e.g., hashing post content or media) and impression tracking (e.g., a Redis bloom filter or seen-item set per user). Discuss where in the pipeline dedup is applied — early for efficiency, with a final pass before delivery to catch cross-source duplicates.
Enforce Privacy Controls
Describe how privacy settings (e.g., audience restrictions, geo-fencing, account visibility) are modeled as metadata attached to content and evaluated during the filtering stage. Emphasize server-side enforcement and the use of a centralized policy service or ACL store to ensure consistency.
Handle User Blocks and Mutes
Explain that block/mute relationships are stored in a low-latency graph or key-value store and applied as a filter pass after candidate generation. Discuss bidirectional block enforcement and the challenge of cache invalidation when a new block is created mid-session.
Discuss Trade-offs and Edge Cases
Address trade-offs such as pre-filtering vs. post-filtering (latency vs. accuracy), stale cache risks, and the cost of real-time vs. batch enforcement. Mention monitoring and auditability to ensure controls are working correctly at scale.
Key Points to Mention
I talked through Redis for the feed cache with a TTL-based eviction and Cassandra for the post store.
Suggested Approach
Start by clarifying the use case and access patterns before jumping into solutions, since caching and sharding strategies are highly dependent on read/write ratios and data characteristics. Structure your answer by first identifying what data needs to be cached, then selecting appropriate storage layers, and finally explaining sharding logic with concrete reasoning. Ground your decisions in Lyft's domain — think ride data, driver locations, pricing, and user sessions.
Clarify Requirements & Access Patterns
Ask about read/write ratios, data size, latency requirements, and consistency needs. For example, driver location data is write-heavy and latency-sensitive, while ride history is read-heavy and can tolerate eventual consistency.
Define Caching Strategy
Choose a caching pattern (cache-aside, write-through, or write-behind) based on consistency requirements, and identify what data to cache — hot data like active ride states, surge pricing zones, or user sessions. Discuss TTL policies and cache invalidation approaches.
Select Storage Technologies
Map each data type to the appropriate storage layer — e.g., Redis for low-latency caching and ephemeral state, Cassandra or DynamoDB for high-throughput distributed storage, and PostgreSQL for transactional ride/payment records. Justify each choice based on the data model and access pattern.
Design the Sharding Strategy
Explain your sharding key selection — for example, sharding ride data by rider_id or region to ensure locality and even distribution. Discuss consistent hashing to minimize data movement during scaling and how to handle hot spots (e.g., popular pickup zones).
Address Failure & Scalability
Discuss replication strategies for high availability, cache eviction policies (LRU, LFU), and how the system handles cache misses or node failures gracefully. Mention monitoring cache hit rates and rebalancing shards as the system scales.
Key Points to Mention
Cold-start I had a decent answer for: show trending or curated content until enough follow graph data exists.
Suggested Approach
Frame your answer around two distinct but related problems: cold-start (no user history) and feed backfilling (latency-sensitive catch-up after a follow action). Start by clarifying constraints like acceptable latency, feed freshness requirements, and scale, then walk through concrete strategies for each problem with trade-offs clearly articulated.
Clarify Requirements & Constraints
Ask about acceptable feed latency, the definition of 'cold' (zero history vs. sparse history), and whether the feed is real-time or near-real-time. Establish scale assumptions like DAU, follow graph size, and post volume.
Address Cold-Start for New Users
Propose a tiered fallback strategy: use onboarding signals (interests, location, contacts) to seed initial recommendations, fall back to popularity-based or trending content, and apply collaborative filtering as soon as minimal interaction data is available.
Design the Feed Backfill Strategy
Explain the push vs. pull trade-off for feed generation, then describe a hybrid approach where following a new account triggers an async backfill job that fetches the N most recent posts and merges them into the user's existing feed store with proper timestamp ordering.
Handle Consistency & Edge Cases
Address potential issues like duplicate posts during backfill, ordering conflicts between real-time and backfilled content, and rate-limiting backfill jobs to avoid thundering herd problems when a celebrity account is followed by millions simultaneously.
Discuss Monitoring & Iteration
Mention metrics to track success such as time-to-first-relevant-content, feed engagement rate for new users, and backfill completion latency. Propose A/B testing different cold-start strategies to iteratively improve recommendation quality.
Key Points to Mention
Talked about fan-out worker failures, cache stampedes, and feed service timeouts.
Suggested Approach
Structure your answer by first identifying the most critical failure modes specific to a feed system (data pipeline failures, ranking model issues, latency spikes), then map each failure to a concrete monitoring strategy with actionable alerts. Ground your response in real-world production thinking by discussing both proactive detection and reactive mitigation, demonstrating that you've thought beyond the happy path.
Identify Core Failure Categories
Enumerate failure scenarios across infrastructure (service crashes, DB unavailability), data pipeline (ingestion lag, missing events), and algorithmic layers (ranking model drift, feature store staleness). Categorizing failures upfront shows systematic thinking.
Define Key Metrics & SLOs
Establish Service Level Objectives such as feed latency p99 < 200ms, feed freshness < 5 minutes, and availability > 99.9%. These metrics become the baseline against which alerts and dashboards are calibrated.
Design Monitoring & Alerting Strategy
Describe a layered monitoring approach: infrastructure metrics (CPU, memory, error rates via tools like Datadog or Prometheus), business metrics (CTR, session length, feed engagement), and data quality checks (null rates, schema drift). Pair each metric with actionable PagerDuty-style alerts.
Plan Fallback & Degradation Strategies
For each failure mode, define a graceful degradation path — for example, serving a cached or popularity-ranked feed if the personalization model is unavailable, or using a circuit breaker to prevent cascading failures. This demonstrates resilience engineering thinking.
Establish Incident Response & Post-Mortems
Outline an on-call runbook for common failure scenarios and commit to blameless post-mortems to capture learnings. Mention canary deployments and feature flags as tools to reduce blast radius during rollouts.
Key Points to Mention
Discussion(7)
Sign in to join the discussion.
Feed freshness lag is an underrated metric and the fact that you mentioned it probably landed well. A lot of candidates only talk about latency and error rates, but for a feed system the question 'how stale is the average feed item at render time' is actually more directly tied to product quality. Cache stampede handling is worth having a concrete answer for too, not just naming it. The standard mitigation is probabilistic early expiration, where you recompute the cache entry slightly before it expires rather than letting every request pile up at expiration time.
Cold-start with trending/curated content is the standard answer and it's fine. The backfill ordering problem is trickier than it looks. When you follow someone and kick off a background job to pull their recent posts, you need a consistent definition of 'recent' that doesn't shift while the job runs. If you just pull 'the last 50 posts by this author' at job start time, and they post something new mid-job, you might end up with a gap or a duplicate depending on your pagination cursor. Using a fixed timestamp anchor at job-start time and paginating backwards from there solves it. The other thing worth mentioning is that backfill should write into the feed with lower priority than real-time fan-out, so you don't have a thunderstorm of backfill writes from a user who just followed 200 people at once contending with live traffic on your feed workers.
The hot shard problem from a viral post is something I got asked almost the same way. Sharding posts by post ID is right because it distributes read load across shards randomly rather than concentrating it. But the follow-up worth anticipating is what happens to your cache layer when a post goes viral and suddenly every user's feed is requesting the same post object. That's where you want a separate post detail cache (Redis, keyed by post ID) that's independent of the per-user feed cache. The feed cache stores ordered lists of post IDs, and the post detail cache stores the actual content. That separation means a viral post hits the post detail cache once and gets served from memory for everyone, rather than each user's feed fetch pulling the full post object from Cassandra. The TTL conversation for feed caches is also worth having: active users get short TTLs because freshness matters, inactive users get longer TTLs or you just evict them and rebuild on next login.
The hybrid answer is correct, but you're right that landing on it reactively looks weaker than opening with it. The way I'd frame it proactively: start by saying fan-out-on-write is your default because read latency is more user-visible than write latency, then immediately flag the celebrity problem as the known failure mode of that approach. Something like 'the tradeoff breaks down when a single write event fans out to tens of millions of feed rows simultaneously, so we need a carve-out.' That way you're not pivoting under pressure, you're just filling in a detail you already anticipated. The threshold question is worth thinking through too. Most systems draw the line somewhere around 10k or 100k followers and treat anyone above that as a 'high-fanout account' stored in a separate metadata table. At read time you merge the precomputed feed for normal accounts with a real-time pull for the celebrity accounts the user follows. The merge step is a small sorted join which is cheap. Where I've seen people stumble is not thinking about what happens when a celebrity account crosses that threshold mid-growth, so having a background job that reclassifies accounts and migrates their fan-out strategy is worth mentioning.
Bloom filters are a reasonable answer for deduplication but the false positive framing is where you need to be precise. A false positive in this context means a post the user hasn't seen gets incorrectly marked as seen and dropped from the feed. That's a real product bug, not just a theoretical accuracy issue. The memory vs accuracy tradeoff is basically: a bloom filter with 1% false positive rate needs about 9.6 bits per element, so for a user who's seen 10 million posts that's roughly 12MB per user, which is way too much to store per-user in memory. So in practice you'd either scope the bloom filter to a recent window (last N posts, not all-time), or use it only as a first-pass filter backed by an exact deduplication check in Redis with a TTL. The exact check is more expensive but you only hit it when the bloom filter says 'maybe seen.' Privacy and block filters being a read-time layer is the right call architecturally because it keeps the write path simple and means you don't have to retroactively clean up feeds when someone gets blocked after the fact.
Picking WebSockets was the right call, just make sure the justification is tight. Long polling works but it hammers your servers with reconnect cycles at scale, and SSE is fine for one-way pushes but WebSockets give you the bidirectional channel you'd want if you ever need client-initiated events like read receipts. The real answer for why WebSockets at Lyft scale is connection multiplexing through something like a dedicated push service that maintains the socket connections separately from your feed service, so you're not holding millions of open sockets on your application servers.
Your instinct to separate write path and read path from the start is exactly right, and I'd go further: sketch them as two completely different flows on the whiteboard before touching anything else. The reason is that the interviewer's mental model of the system is almost always organized around those two paths, so when you jump straight into a unified architecture diagram, you end up talking past each other. The data model question is where I've gotten burned before too. Indexing feels like a detail until someone asks 'how does this query actually run' and you realize your schema requires a full table scan. For a feed system specifically, the access patterns you need to nail early are: fetch recent posts for a user's feed (sorted by time or score), fetch all posts by a given author, and look up a post by ID. Each of those implies a different index or even a different table. On the non-functional side, the capacity estimate nudge from your interviewer was probably them trying to anchor the rest of the conversation. Feed systems live and die on read volume, so getting to 'we have X reads per second, which means our cache hit rate needs to be Y' early gives every later decision a concrete justification rather than a vibes-based one. I'd spend maybe three to four minutes on that before touching the data model at all.