Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 85

Mock System Design Interview Practice

Recommended resources

Welcome to Lesson 3 of our final module, "Interview Readiness and Practical Application." In our last lesson, you executed a full-length mock coding interview. You learned to apply a structured framework to deconstruct a problem, analyze trade-offs between data structures, and perform under pressure.

Today, we transition from the microcosm of algorithms to the macrocosm of large-scale systems. A system design interview is fundamentally different from a coding one. It's less about finding a single optimal algorithm and more about navigating a vast space of trade-offs, making reasoned architectural decisions, and communicating a high-level vision. Your background in data science and distributed systems will be a significant asset here.

This lesson directly addresses the learning outcome: Execute a full-length mock interview focused on high-level system design.

We will follow your preferred 5-part structure, which maps perfectly to the flow of a real system design interview:

  1. The Framework: A Blueprint for System Design
  2. The Prompt & Requirements Gathering: Deconstructing the Problem
  3. High-Level Design: Sketching the Architecture
  4. Deep Dive: Tackling Scale, Latency, and Availability
  5. The Debrief: From Senior to Staff-Level Signals

Let's begin.


1. The Framework: A Blueprint for System Design (Easy)

(Tutor speaking)

Before we begin the simulation, we need a robust framework. Unlike a coding problem that has a clear "correct" answer, a system design problem is open-ended. Success depends on your ability to manage ambiguity, structure the conversation, and justify your choices. A framework is not a script, but a guide to ensure you cover all critical aspects methodically.

To internalize what a world-class framework looks like, let's review the standard methodology that top-tier companies expect.

System Design Interview Framework for Distributed Systems

The article 'System Design Interview Framework for Distributed Systems' from Design Gurus provides a comprehensive, step-by-step guide. It breaks down the interview into logical phases that we will emulate today.

Read the entire article, paying close attention to the six main steps of the framework: Clarify Requirements, High-Level Architecture, Design Core Components, Address Key Considerations, Discuss Trade-offs, and Summarize. This is the blueprint for our interview.

  • Metaphor for a 5-Year-Old: Imagine you're asked to build a huge Lego city.
    • Clarify Requirements: You first ask, "Should it have an airport? A police station? How many people will live here?" You figure out what needs to be in the city.
    • High-Level Design: You draw a map. "The airport goes here, the houses go here, and a big highway connects them."
    • Deep Dive: You focus on one tricky part, like the airport. "The runway needs to be long, we need a control tower, and gates for the planes."
    • Trade-offs: You think, "I can build one huge runway to land giant planes, but it will be expensive. Or I can build two smaller runways, which is cheaper but can't handle the biggest planes." You explain why you chose one way.

To help you visualize these concepts, here are two excellent cheat sheets that summarize the key components and frameworks.

System Design Interview Cheat Sheet
This cheat sheet summarizes the interview framework, API design choices (REST, RPC, GraphQL), and core scalability concepts like replication, sharding, and caching.
The RESHADED Method for System Design Interviews
This image outlines the 'RESHADED' method, another structured approach, and provides a glossary of common system design building blocks and their dependencies.

Now, let's switch to interview mode.


(Interviewer speaking)

"Hi, thanks for coming in today. I'm a Staff Engineer on the cloud storage team. We'll spend the next 45 minutes designing a large-scale system. I'm more interested in your thought process and how you handle trade-offs than in a single 'perfect' solution, so please think out loud.

Let's get started. The problem is: Design a URL shortening service like TinyURL or bit.ly."



2. The Prompt & Requirements Gathering: Deconstructing the Problem (Medium)

(Interviewer speaking)

"So, to reiterate: Design a URL shortening service. Take a few minutes to think about it, and then walk me through how you'd approach this problem."

Your Turn (Guided Thinking):
This is the Clarify Requirements phase. Before drawing a single box, what questions must you ask? What assumptions must you validate?

  • Functional Requirements (What it does):

    • Core: Given a long URL, return a short URL. Given a short URL, redirect to the original long URL.
    • Features: Should we support custom short URLs? Do URLs expire? Should we track analytics (e.g., click count)?
    • Let's scope it: For today, we'll focus on the core functionality plus analytics. We'll leave custom URLs and expiration for later.
  • Non-Functional Requirements (How well it does it):

    • Availability: This is critical. If our service is down, all shortened links on the internet break. It needs to be highly available.
    • Latency: The redirect must be extremely fast. Users should barely notice it.
    • Scalability: How large is this system? This is where you do back-of-the-envelope estimations.

Let's quantify the scale. Assume we're designing a major service.

  • Write Traffic (New URLs): Let's estimate 100 million new URLs created per month.
    • Writes per second (QPS):
  • Read Traffic (Redirects): Let's assume a 100:1 read-to-write ratio, which is common for content services.
    • Reads per second (QPS): (and this could easily spike to 10x that).
  • Storage (over 5 years):
    • Total URLs:
    • Data size per URL: short_url (e.g., 7 chars) + long_url (avg. 500 chars) ≈ 500 bytes.
    • Total Storage:

This initial analysis is crucial. It tells us that the system is read-heavy, must be highly available, and while storage isn't petabyte-scale, 6 billion records is too large for a single database server.



3. High-Level Design: Sketching the Architecture (Medium-Hard)

(Interviewer speaking)

"Excellent. You've established clear requirements and quantified the scale. Now, let's sketch out the high-level architecture. What are the main components and how do they interact? And what would the API look like?"

Your Turn (Guided Thinking):
This is the High-Level Architecture and API Design phase.

API Endpoints:

  1. URL Creation: POST /api/v1/url
    • Request Body: { "longUrl": "https://very-long-url.com/with/path" }
    • Success Response (201): { "shortUrl": "http://tiny.url/fB8aN3d" }
  2. URL Redirection: GET /{shortCode} (e.g., GET /fB8aN3d)
    • Success Response: HTTP 301 Moved Permanently redirect to the original longUrl.

High-Level Diagram:

  1. Client: A user's browser or mobile app.
  2. Load Balancer (LB): Distributes incoming traffic across our servers.
  3. Application Service: A set of stateless servers that handle the core logic.
  4. Database (DB): A persistent storage system for the URL mappings.

Core Logic: Generating the shortCode
This is the heart of the system.

  • Approach 1: Hash the long URL. Use a hash function like MD5 or SHA256 on the longUrl and take the first 7 characters.
    • Pros: Simple, stateless. If two people shorten the same URL, they get the same short URL.
    • Cons: Collisions are possible. Two different longUrls could hash to the same shortCode. Handling this is complex.
  • Approach 2: Use a counter. Maintain a single, incrementing number (e.g., 1, 2, 3...). Convert this number into a base-62 string ([a-zA-Z0-9]).
    • Pros: Guaranteed unique shortCode for every new URL. The length of the code also grows slowly.
    • Cons: Requires a stateful counter, which can be a bottleneck. How do we scale this?

We'll start with Approach 2 as it guarantees uniqueness, and we'll tackle the scaling challenge in the deep dive.

Database Schema:
A table urls with columns:

  • short_code (VARCHAR, Primary Key)
  • long_url (TEXT)
  • created_at (TIMESTAMP)

An analytics table clicks:

  • short_code (VARCHAR, Foreign Key)
  • timestamp (TIMESTAMP)
  • ip_address (etc.)


4. Deep Dive: Tackling Scale, Latency, and Availability (Hard)

(Interviewer speaking)

"This is a solid start. You've identified the base-62 approach and a simple schema. However, you noted that the single counter is a bottleneck and that 6 billion records won't fit on one database. Let's tackle those. How would you scale this system to meet your estimated QPS and storage needs?"

Your Turn (Guided Thinking):
This is the Address Key Considerations and Discuss Trade-offs phase.

1. Scaling the Database:

  • Problem: 3 TB of data and 4000+ reads/sec is too much for a single machine.
  • Solution: We need to partition (shard) the database.
  • Sharding Strategy:
    • We could shard by created_at (range-based), but this creates a "hotspot" since all new writes go to the latest shard.
    • A better approach is to shard based on a hash of the short_code. This distributes traffic evenly across all shards. Given your CS background, you'll recognize this is a classic application of hashing for partitioning a key space.
  • Database Choice:
    • A relational database (like PostgreSQL) could work, but managing sharding manually is complex.
    • A NoSQL database like DynamoDB or Cassandra is a natural fit. They are designed for horizontal scaling and handle partitioning automatically. Let's choose a NoSQL key-value store where key = short_code and value = long_url. This is highly optimized for our primary read query.

2. Scaling the ID Generator:

  • Problem: A single counter in a database is a central bottleneck and a single point of failure.
  • Solution: Create a dedicated ID Generation Service.
  • Design: A service that provides unique, roughly sequential IDs. A famous real-world example is Twitter's Snowflake. It generates 64-bit IDs by combining:
    • Timestamp: (41 bits) - time-sortable.
    • Machine ID: (10 bits) - allows multiple machines to generate IDs without coordination.
    • Sequence Number: (12 bits) - handles multiple IDs generated on the same machine in the same millisecond.
      This decentralized approach eliminates the single point of failure and allows our write path to scale horizontally.

3. Optimizing Read Latency:

  • Problem: Even with a sharded DB, 4000+ QPS can be demanding, and network calls to the DB add latency.
  • Solution: Introduce a Caching Layer.
  • Design: Use an in-memory cache like Redis or Memcached. The application service checks the cache first.
    • Cache Hit: If short_code is in the cache, return the long_url immediately. This is extremely fast.
    • Cache Miss: If short_code is not in the cache, query the DB, store the result in the cache, and then return it.
  • Cache Eviction: Since we can't store all 6 billion URLs in cache, we need a policy like Least Recently Used (LRU) to evict older entries. Given our read-heavy pattern, a significant portion of traffic will hit popular URLs, making the cache highly effective.

Let's watch how a professional mock interview handles a similar discussion about scaling, from a naive approach to a robust one.

System Design Interview: Design YouTube w/ a Ex-Meta Staff Engineer

In this 'Design YouTube' mock interview, watch how the engineer evolves the design to handle large files and low latency. The principles—moving from a simple download to chunking, transcoding, and using a CDN—are identical to how we're evolving our URL shortener design by adding sharding, a dedicated ID service, and caching.

Watch from 19:34 to 38:12. Focus on the thought process: (19:34 - 24:03): Identifying the limitation of a simple design (downloading a whole large file) and introducing chunking. This is analogous to us identifying the single DB as a bottleneck and introducing sharding. (24:03 - 34:45): Addressing low-latency requirements by adding transcoding and adaptive streaming. This is analogous to our caching strategy to reduce read latency. (31:00 - 34:45): Introducing a CDN to cache content closer to the user, a further optimization for latency that we can also apply.



5. The Debrief: From Senior to Staff-Level Signals (Hardest)

(Interviewer role ends, Tutor speaking again)

Excellent. The simulation is complete. You've designed a scalable, available, and low-latency URL shortener. Now for the most important part: the debrief. Let's analyze what distinguishes a good answer from a great one.

A Senior Engineer will typically design the system we just outlined: base-62 ID generation, a sharded NoSQL database, and a caching layer. This is a strong, correct answer.

A Staff-Plus Engineer goes further, demonstrating a deeper, more holistic view of the system. They proactively discuss second-order effects and operational concerns.

  • Deeper Trade-off Analysis: They might question the 301 redirect. "A 301 is permanent and gets cached heavily by browsers. What if a user wants to change the destination of a shortUrl? A 302 Found redirect is temporary and gives us more control, at the cost of slightly more traffic since browsers won't cache it as aggressively."
  • Operational Excellence: They think about running the system in production. "How do we monitor this? We need dashboards for QPS, latency percentiles (p95, p99), and cache hit rates. We need alerts for when the ID service is close to exhausting its sequence numbers for a given millisecond."
  • Handling Edge Cases & Abuse: "What about vanity URLs (tiny.url/my-name)? This requires a separate flow and can't use the Snowflake generator. How do we prevent spammers from consuming all our short URLs? We need rate limiting on the POST endpoint, likely by IP address or user account."
  • Data Lifecycle: "What happens to URLs that are never clicked? We could implement a data lifecycle policy to archive or delete URLs that haven't been accessed in over two years to manage storage growth."

To see this in action, let's analyze the feedback given in a real staff-level mock interview.

OpenAI & Meta Senior/Staff System Design Mock Interview: Design Online Auction

In this 'Design Online Auction' mock interview, the interviewer provides phenomenal feedback on what separates a good answer from a staff-level one. This is exactly what we're talking about.

Watch the feedback sections from 39:33 to 51:04 and 57:15 to 1:00:20. Pay attention to: The interviewer's praise for the candidate's structured approach and clear articulation of trade-offs. The introduction of a non-functional requirement the candidate missed: durability (using a queue like Kafka for incoming bids). This shows thinking about failure modes. The advice on demonstrating technical depth: knowing 'under the hood' details of a chosen technology (e.g., Redis LUA scripts) to showcase expertise. This is a key staff-level signal.

Test your understanding!

In our URL shortener design, the write path is:

  1. Client sends longUrl to Application Service.
  2. App Service gets a unique ID from the ID Generation Service.
  3. App Service converts ID to base-62 shortCode.
  4. App Service writes (shortCode, longUrl) to the Database.
  5. App Service returns shortUrl to the client.

What happens if Step 4 fails after Step 2 succeeds? How would you use a message queue, similar to the durability suggestion in the auction video, to make this write path more robust?

Show answer

If Step 4 fails, we have "used" a unique ID from the ID generator, but no URL was stored. This creates a "hole" in our ID space and the user gets an error.

A more robust, staff-level design would use a message queue (like Kafka or SQS) for durability:

  1. The Application Service receives the request and does one quick, simple thing: it publishes a message like { "longUrl": "..." } to a queue and immediately returns a 202 Accepted response to the user, perhaps with a link to poll for the result.
  2. A separate fleet of worker services consumes messages from this queue.
  3. Each worker gets a unique ID, creates the shortCode, and writes it to the database. This is the same logic as before, but it's now an asynchronous background job.

Trade-offs:

  • Pros: The system is more resilient. If the database is temporarily down, the messages just queue up and are processed when it recovers. The write API is also faster because it's not waiting for a database write. This is a classic example of decoupling components for fault tolerance and improved latency.
  • Cons: The design is more complex (we've added a queue and a new worker service). The user doesn't get the shortUrl immediately (eventual consistency), which might be a negative user experience. You would have to justify this trade-off.


Conclusion

Today you simulated a full high-level system design interview. You learned that the process is as important as the result. A structured framework, clear communication, and a deep understanding of trade-offs are what separate an average candidate from an elite one.

Key Takeaways:

  • The Framework is Non-Negotiable: Always start with requirements and estimations. This grounds your entire design.
  • Quantify Everything: Back-of-the-envelope math transforms vague problems into concrete engineering challenges.
  • Design for Failure: Elite engineers don't just design for the happy path. They proactively discuss bottlenecks, single points of failure, and how the system can handle them.
  • Trade-offs are the Answer: There is no "perfect" technology. The best answer always explains why a certain database, caching strategy, or communication protocol was chosen over alternatives for this specific problem.
  • Think Operationally: A staff-level mindset includes thinking about monitoring, deployment, security, and the long-term maintenance of the system.

Next Lesson Preview

You have now tackled mock interviews for both of the main technical pillars: algorithms and system design. In our next lesson, we will focus on executing a full-length mock interview focused on a domain-specific area, such as the ML or Data Platform design you are passionate about. This will allow you to combine your foundational skills with your specialized expertise.