Imagine you're opening the general channel of a large Discord server.
That channel has existed for years.
It contains:
10,000,000+ messagesYou click the channel.
It opens almost instantly.
How?
Surely Discord didn't download all 10 million messages.
That would require hundreds of megabytes of data, consume huge amounts of memory, and take several seconds before you could even read the latest message.
So what actually happens?
The Naive Solution
A beginner implementation might look like this.
SELECT *
FROM messages
WHERE channel_id = 123
ORDER BY created_at;The backend sends every message.
The browser renders everything.
Congratulations.
Your browser just crashed.
Even if the database somehow returned the data quickly, rendering millions of DOM elements would freeze the UI.
The problem isn't just the database.
It's the network, memory, and browser rendering.
Fetch Only What The User Can See
When you open a Discord channel, you don't need 10 million messages.
You only need the most recent few.
Instead of fetching everything...
Discord requests something like:
GET /messages?limit=50The backend returns only:
Message 9,999,951
↓
Message 10,000,000Just enough to fill your screen.
The rest simply doesn't exist yet from the client's perspective.
What Happens When You Scroll?
Suppose you scroll upward.
Instead of downloading the entire chat history...
Discord requests the next batch.
GET /messages
?before=9999951
&limit=50The server returns:
9999901
↓
9999950Scroll again.
Another request.
This technique is called Lazy Loading.
Only fetch data when the user actually needs it.
Why OFFSET Doesn't Scale
Many engineers first think of SQL pagination.
SELECT *
FROM messages
ORDER BY id DESC
LIMIT 50
OFFSET 5000000;Looks fine.
Until there are millions of rows.
The database still has to skip five million records before returning the next fifty.
Large offsets become slower and slower.
Performance degrades dramatically.
Cursor Pagination
Modern applications rarely use large offsets.
Instead, they use cursors.
Suppose the oldest visible message has:
Message ID
98347291The next request becomes:
GET /messages
?before=98347291
&limit=50The database executes:
SELECT *
FROM messages
WHERE id < 98347291
ORDER BY id DESC
LIMIT 50;The database jumps directly to the correct position using an index.
No scanning millions of rows.
Performance stays almost constant even with billions of messages.
Infinite Scroll
Notice something interesting.
Discord doesn't even know how many pages exist.
There are no buttons like:
Page 1
Page 2
Page 3Instead...
Scrolling itself becomes the pagination mechanism.
User Scrolls
↓
Fetch More Messages
↓
Append To UIThis creates a seamless experience.
Users never think about pages.
Don't Render Everything
Even after loading 2,000 messages...
Discord still doesn't render all 2,000.
Only the messages currently visible on your screen remain mounted.
As you scroll...
Old messages are removed from the DOM.
New ones appear.
This technique is called Virtualization.
Instead of rendering thousands of components...
The browser only renders around:
30–50 messagesMemory stays low.
Scrolling remains smooth.
New Messages While You're Reading
Suppose you're reading old messages.
Someone sends a new one.
Should Discord suddenly jump you to the bottom?
No.
Instead...
It shows:
↓
23 New MessagesOnly when you click it does the UI scroll to the latest message.
Small UX decisions like this make the application feel polished.
What About Images?
Messages often contain images, videos, and GIFs.
Should Discord download every attachment immediately?
No.
Images are loaded only when they enter the viewport.
This reduces:
- Bandwidth
- Memory usage
- Initial loading time
The same technique powers Instagram, Reddit, and X.
What Happens If The User Searches?
Searching millions of messages by downloading them all would be impossible.
Instead...
The search request goes directly to the backend.
GET /search
?q=deploymentThe database (or a dedicated search engine like Elasticsearch) performs the search and returns only matching results.
The client never scans the entire history.
Follow-Up Questions Interviewers Love
Why not use OFFSET?
OFFSET becomes increasingly expensive as tables grow because the database must skip every preceding row.
Cursor pagination scales much better.
Why is cursor pagination faster?
Because indexed lookups allow the database to jump directly to the next set of rows instead of scanning everything before them.
Why use infinite scroll instead of page numbers?
Users consume chats continuously.
Infinite scrolling feels natural and avoids unnecessary page navigation.
Why virtualize the UI?
Even if the backend is fast, rendering thousands of DOM elements can freeze the browser.
Virtualization keeps the UI responsive.
Lessons Beyond Discord
The same techniques power many large applications.
- WhatsApp chat history.
- Slack channels.
- Reddit comments.
- LinkedIn feeds.
- GitHub Issues.
- X timelines.
- Instagram comments.
Different products.
The same scalability patterns.
Final Thoughts
Discord doesn't feel fast because it has powerful servers.
It feels fast because it avoids unnecessary work.
It doesn't download millions of messages.
It doesn't render thousands of components.
It doesn't paginate using expensive offsets.
Instead, it fetches only what you can see, loads more only when needed, and lets efficient database indexes do the heavy lifting.
Sometimes the fastest system isn't the one that processes the most data.
It's the one that knows what not to process at all.