DEV Community

Niyati Nehal
Niyati Nehal

Posted on

Everyone Says “Clarify the Requirements” in System Design. But How?

You all might have watched a lot of YouTube videos on system design and heard almost every one of them say:

“Before you start designing, clarify the functional and non-functional requirements.”
Enter fullscreen mode Exit fullscreen mode

And yes, obviously.
You’re supposed to clarify the requirements.
But how do you actually do that?
What exactly do you ask the interviewer?
How do you know whether the question you’re asking is useful?

How do you go from:

“Design a URL shortener”
Enter fullscreen mode Exit fullscreen mode

to actually knowing what you’re supposed to build?
I think this is one of those things that sounds very obvious when someone explains it after the fact, but when you’re actually sitting in an interview, it’s much harder.

So I wanted to break down the five things we usually hear about:

1-Functional and non-functional requirements
2-API design
3-High-level design|
4-Database design
5-Detailed design

But not as a checklist.

More importantly, how do you arrive at each of these? What questions do you ask, and why?
First: Don’t Start Designing

The interviewer says:

“Design a URL shortener.”
Enter fullscreen mode Exit fullscreen mode

My brain might immediately go:
“Okay, Redis, database, load balancer…”

But wait.
I don’t even know what I’m designing yet.
The problem statement is intentionally vague.
So the first thing I’m actually trying to do is:
remove ambiguity.

I want to turn:

“Design a URL shortener”
Enter fullscreen mode Exit fullscreen mode

into something more like:

“We’re building a URL-shortening service for authenticated users, supporting URL creation and redirection, with X traffic, Y latency requirements, and Z availability requirements.”
Enter fullscreen mode Exit fullscreen mode

Now I have something I can actually design.
And this is where those five things come in.

  1. Functional Requirements: What Does the User Actually Need to Do?

The usual advice is:

“Clarify the functional requirements.”
Enter fullscreen mode Exit fullscreen mode

Okay.
But I don’t want to ask the interviewer:

“What are the functional requirements?”
Enter fullscreen mode Exit fullscreen mode

That’s technically correct, but it doesn’t really help me think.
I’d rather start with:
Who is using the system?

For a URL shortener:

“Are users authenticated, or can anyone create a short URL anonymously?”
Enter fullscreen mode Exit fullscreen mode

That one question already changes things.
If users are authenticated, I might need:

user accounts
ownership
authorization
URL management
user-specific limits
Enter fullscreen mode Exit fullscreen mode

If it’s anonymous, a lot of that disappears.
So I’m not asking questions just to collect information.
I’m asking:

“What information could actually change my design?”
Enter fullscreen mode Exit fullscreen mode

Then ask: What can the user actually do?

For example:

“Is the requirement only to create and redirect short URLs, or do we also need expiration, custom aliases, analytics, deletion, etc.?”
Enter fullscreen mode Exit fullscreen mode

Because “URL shortener” could mean a very simple service.
Or it could mean:

create URLs
delete URLs
custom aliases
expiration
analytics
user dashboard
authentication
Enter fullscreen mode Exit fullscreen mode

I need to know where the boundary is.
And this applies to every system.
If someone says:

“Design Instagram.”
Enter fullscreen mode Exit fullscreen mode

That could mean the entire Instagram product.
Obviously we’re not designing all of Instagram in an interview.
So I need to establish the scope.
Find the Main User Journey

Once I know the scope, I want to understand the most important thing the user is trying to do.

For a URL shortener:

User

Submits long URL

System generates short code

User gets short URL

Someone opens short URL

System finds original URL

User is redirected

That’s the core flow.
Now I have something around which I can start thinking.
And this gives me a useful rule:

Don’t try to understand every possible feature first. Find the primary user journey.
Enter fullscreen mode Exit fullscreen mode
  1. Non-Functional Requirements: How Well Does It Need to Work?

Okay, now I know what the system does.

The next question is:
How well does it need to do it?

This is where non-functional requirements come in.
And again, I don’t want to ask:

“What are the NFRs?”
Enter fullscreen mode Exit fullscreen mode

I want to ask questions that reveal the constraints.
How much traffic?
Suppose the interviewer says:

“We have millions of users.”
Enter fullscreen mode Exit fullscreen mode

Cool.
But that doesn’t tell me much.
Millions of users doesn’t mean millions of requests per second.
So I’d ask:

“How many daily active users are we expecting?”
Enter fullscreen mode Exit fullscreen mode

Then:

“Approximately how many requests per second?”
Enter fullscreen mode Exit fullscreen mode

Then:

“What’s the read-to-write ratio?”
Enter fullscreen mode Exit fullscreen mode

And:

“Is that average traffic or peak traffic?”
Enter fullscreen mode Exit fullscreen mode

Suppose I get:

1,000 URL creations/sec
100,000 redirects/sec
Enter fullscreen mode Exit fullscreen mode

Now I know something important.
The system is very read-heavy.
That immediately makes things like caching and read scaling relevant.
Notice what happened:
I didn’t decide:

“Let’s use Redis.”
Enter fullscreen mode Exit fullscreen mode

The traffic requirement led me toward that discussion.
What About Latency?

Instead of vaguely asking:

“What’s the latency requirement?”
Enter fullscreen mode Exit fullscreen mode

I can ask something more concrete:

“For the redirect operation, are we okay with something like 100 ms, or do we need it to be closer to tens of milliseconds?”
Enter fullscreen mode Exit fullscreen mode

Now I can start thinking about what belongs in the critical path.
If something is expensive but doesn’t need to happen before the user receives a response, maybe it can happen asynchronously.
Again:

Requirement → consequence → architecture.
What Absolutely Needs to Work?

Availability is another thing I don’t want to treat as a number in isolation.

I can ask:

“If the analytics system is temporarily unavailable, should URL redirection still work?”
Enter fullscreen mode Exit fullscreen mode

If the interviewer says yes, then I’ve learned something important:

Redirection is critical. Analytics isn’t.

So maybe analytics shouldn’t be part of the synchronous redirect path.
Maybe I can process analytics asynchronously.
That’s a much more useful way of thinking about availability than simply memorizing:

“The system should have 99.99% availability.”
Enter fullscreen mode Exit fullscreen mode

And Then There’s Consistency

One question I like here is:

“What happens if two users temporarily see different values?”
Enter fullscreen mode Exit fullscreen mode

Because not everything needs the same consistency guarantees.
A social-media like count being slightly stale might be acceptable.
A bank balance being wrong is a very different problem.
So instead of memorizing:

“This uses eventual consistency and this uses strong consistency.”
Enter fullscreen mode Exit fullscreen mode

Ask:

“What happens if the data is temporarily inconsistent?”
Enter fullscreen mode Exit fullscreen mode

The answer tells me how strong my consistency requirements actually need to be.

  1. API Design: Don’t Just Design Endpoints

Now that I understand what the system needs to do, I can think about:

How does the outside world interact with it?
Enter fullscreen mode Exit fullscreen mode

Suppose I need to create a short URL.
First ask:

“What does the client need to send?”
Enter fullscreen mode Exit fullscreen mode

Probably:

{
"url": "https://example.com"
}

Then:

“What does the client need back?”
Enter fullscreen mode Exit fullscreen mode

Maybe:

{
"shortUrl": "https://short.ly/abc123"
}

Only now does something like:

POST /urls

start making sense.
I’m not inventing the endpoint.
I’m deriving it from the user action.
But Don’t Stop at the Happy Path

For every important API, I want to ask:
What is the action?

Create a short URL.
What information does it need?

The original URL.
What does it return?

The generated short URL.
What can go wrong?

Invalid URL
Rate limit
Duplicate request
Service failure
Enter fullscreen mode Exit fullscreen mode

And then one of my favourite questions:
What happens if the client retries?

Imagine this:
Become a Medium member

The client sends a request.
The server successfully creates the short URL.
But the response gets lost.
The client doesn’t know whether the request succeeded.
So it sends the request again.
What happens?
Do we create another short URL?
Or do we return the previous result?
Now we’ve naturally arrived at:

idempotency.

This is why I don’t think API design should just be:

“Let’s design some REST endpoints.”
Enter fullscreen mode Exit fullscreen mode

The API is where the system’s behaviour starts becoming explicit.

  1. HLD: Stop Drawing Boxes Just Because You Know Them

This is probably where I’ve seen the most “system design interview diagrams.”

Something like:

Client

Load Balancer

API Gateway

Microservices

Redis

Kafka

Database

And then everyone nods because it looks like a system.
But the interviewer can simply ask:

“Why do you need Kafka?”
Enter fullscreen mode Exit fullscreen mode

And suddenly we’re in trouble.
So I’ve started thinking about HLD differently.
Before adding a component, ask:

What problem does this component solve?
Enter fullscreen mode Exit fullscreen mode

Example: Why would I need a queue?

Suppose I’m designing a video-sharing platform.
Video processing can take a long time.
I don’t want the user to sit there waiting for:

transcoding
generating different resolutions
thumbnails
metadata processing
Enter fullscreen mode Exit fullscreen mode

So I might design:

Upload

Object Storage

Create Processing Job

Queue

Workers

Processed Videos

Now the queue has a reason to exist.
It’s not there because:

“Kafka is something senior engineers use.”
Enter fullscreen mode Exit fullscreen mode

It’s there because:

I have expensive work that doesn’t need to block the user’s request.
Enter fullscreen mode Exit fullscreen mode

That’s the kind of explanation I want to be able to give.
Every Box Should Have a “Why”

If I draw Redis:

Why?
Enter fullscreen mode Exit fullscreen mode

Because the data is frequently read and can be cached.
If I draw Kafka:

Why?
Enter fullscreen mode Exit fullscreen mode

Because work can happen asynchronously and doesn’t need to block the request.
If I draw a separate service:

Why?
Enter fullscreen mode Exit fullscreen mode

Because it has a separate responsibility, scaling requirement, or failure boundary.
If I can’t explain why a component exists, maybe I shouldn’t have drawn it.

So my rule here is:

Architecture should be the consequence of requirements, not a collection of technologies I remember.
Enter fullscreen mode Exit fullscreen mode
  1. Database Design: Don’t Start With “SQL or NoSQL?”

This is another place where I think it’s easy to jump to the wrong question.

Instead of:

“Should I use PostgreSQL or MongoDB?”
Enter fullscreen mode Exit fullscreen mode

Start with:

What information does the system actually need to remember?
Enter fullscreen mode Exit fullscreen mode

For a URL shortener:

original URL
short code
user
creation time
expiration time
Enter fullscreen mode Exit fullscreen mode

Then ask:

How do I need to access this information?
Enter fullscreen mode Exit fullscreen mode

Maybe the most important query is:

shortCode → originalURL

That’s more useful to me than immediately debating databases.

Then I can ask:

How often do we run this query?
How much data will we have?
How quickly will it grow?
What relationships exist?
Do we need transactions?
What consistency do we need?
Enter fullscreen mode Exit fullscreen mode

Only after answering these questions should I start thinking about the storage technology.
So the mental sequence becomes:

Data → relationships → queries → query frequency → scale → consistency → storage choice

Not:

Problem → MongoDB because it’s scalable.

  1. Detailed Design: Where Is the Interesting Problem?

At this point, I should know:

what we’re building
who uses it
how much traffic we have
what the important APIs are
what the major components are
what data we store
Enter fullscreen mode Exit fullscreen mode

Now I can zoom in.
But I don’t need to explain every single class or function.
Instead, ask:

What’s the hardest part of this system?
Enter fullscreen mode Exit fullscreen mode

For a URL shortener, maybe it’s:

“How do we generate unique short codes when multiple servers are creating URLs at the same time?”
Enter fullscreen mode Exit fullscreen mode

Now we can go deeper.

Request

Generate unique ID

Base62 encoding

Short code

Store mapping

And now more questions appear:
What if two servers generate the same ID?
Can IDs be generated independently?
Do we need a centralized ID generator?
What happens if the ID generator goes down?
What happens if two identical requests arrive simultaneously?
Now I’m actually doing detailed system design.
The Most Important Thing: Every Answer Should Create the Next Question

This is probably the biggest thing I’m taking away from all of this.

System design shouldn’t feel like:

Question → answer → next section.
Enter fullscreen mode Exit fullscreen mode

It should feel like:

Question → answer → consequence → next question → design decision
Enter fullscreen mode Exit fullscreen mode

For example:

The interviewer says:

“We expect 1 million requests per second.”
Enter fullscreen mode Exit fullscreen mode

I ask:

“Are those mostly reads or writes?”
Enter fullscreen mode Exit fullscreen mode

They say:

“Mostly reads.”
Enter fullscreen mode Exit fullscreen mode

I ask:

“Can the data be cached?”
Enter fullscreen mode Exit fullscreen mode

They say:

“Yes, it doesn’t change frequently.”
Enter fullscreen mode Exit fullscreen mode

Now I think:

“Okay, caching can reduce the load on the database.”
Enter fullscreen mode Exit fullscreen mode

Then I ask:

“What happens on a cache miss?”
Enter fullscreen mode Exit fullscreen mode

Now I’m thinking about the database.

Then:

“Can the database handle the remaining traffic?”
Enter fullscreen mode Exit fullscreen mode

If not:

“Can we use read replicas?”
Enter fullscreen mode Exit fullscreen mode

Notice how the architecture is slowly appearing.
I didn’t start with:

“Let’s use Redis.”
Enter fullscreen mode Exit fullscreen mode

The requirement led me there.
So What Are the Five Things Actually Doing?

Maybe this is the easiest way for me to remember them.

  1. Functional requirements

What are we building?
Who uses it?
What can they do?
What is the main user journey?
What’s in scope?
What’s out of scope?

  1. Non-functional requirements

How well does it need to work?
How much traffic?
How much data?
What latency?
What availability?
What consistency?
What happens during failures?

  1. API design

How does the outside world interact with it?
What does the client send?
What does it receive?
What can go wrong?
What happens on retries?
Is it idempotent?

  1. HLD

What pieces do I need, and why?
Cache?
Queue?
Database?
Workers?
Services?
Load balancer?
And most importantly:

Why?

  1. Database + detailed design

How does the system actually store and process information?
What are the entities?
What are the important queries?
What happens under concurrency?
What happens when something fails?
Where is the bottleneck?
The Question I Want to Keep Beside Me

I don’t think I need to memorize a giant list of system-design questions.
I just need to keep asking:

“What do I need to know before I can make this decision?”
Enter fullscreen mode Exit fullscreen mode

I need to choose a database?
First understand the data and access patterns.
I need to add Redis?
First understand whether caching solves an actual problem.
I need Kafka?
First understand whether something should be asynchronous.
I need a read replica?
First understand the read volume and database bottleneck.
I need strong consistency?
First understand what happens if users temporarily see different data.
That’s probably the biggest shift in how I want to approach system design.
Not:

“What architecture do I know for this problem?”
Enter fullscreen mode Exit fullscreen mode

But:

“What questions do I need to answer so that the architecture becomes obvious?”
Enter fullscreen mode Exit fullscreen mode

And honestly, that’s probably what I should be practicing when I practice system design.
Not drawing more diagrams.
Getting better at asking the next useful question

Top comments (0)