← Back to list

Why Telegram Feels So Freaking Fast (And How You Can Steal Its Tricks)

You open Telegram. Tap a chat. Type something. Hit send.

Kyryll · 2026-07-20 09:50 · 0 claps · 7.6 min read
#software-engineering #software-development #telegram #distributed-systems #optimization
Open on Medium ↗

Why Telegram Feels So Freaking Fast (And How You Can Steal Its Tricks)

You open Telegram. Tap a chat. Type something. Hit send.

And it’s just… there. No spinner. No little “sending” ghost-text you have to wait out. The message shows up sent before your brain has even finished processing that you tapped the button. Scroll through a chat with ten thousand messages in it and nothing hitches. Open a group with fifty thousand members and the app doesn’t so much as blink.

Most chat apps do not feel like this. WhatsApp is fine. Slack, on a bad day, feels like it’s dragging a suitcase behind it. So what is Telegram actually doing differently — and can the rest of us steal it?

Short answer: yes, mostly. Long answer is the rest of this article.

The lazy answer, which is wrong

“They use a custom protocol, that’s why.” True, technically, and also almost useless as an explanation — it’s the kind of thing people repeat without knowing what it actually buys you. And it skips the part that explains most of what you feel as a user, which barely has anything to do with the network protocol and everything to do with what the app does before it even talks to a server.

There are four separate decisions stacked on top of each other here. Pull any one out and the app keeps working — it just stops feeling like magic.

1. It basically never waits on the network to show you something

This is the main one, and nobody talks about it enough.

Telegram’s clients all sit on top of TDLib, an open-source C++ core shared across every official app — Android, iOS, desktop, all of it. TDLib keeps a full, encrypted copy of your chats sitting locally on your device. So when you open a conversation, the app isn’t going “hey server, gimme the last 50 messages” and then twiddling its thumbs. It’s just reading off local disk, which is already synced. The actual syncing with the server happens quietly in the background, like a roommate who does the dishes before you notice they’re dirty.

This is why history loads instantly. You’re not paying network latency to read — only to write, and even writes get faked (in the best way) before they’re real. Compare that to the standard way apps get built: component mounts, fires a request, throws up a skeleton loader, waits, renders. Every app built that way has a hard floor on how fast it can ever feel, and that floor is set by your network round trip. Telegram basically doesn’t have that floor for anything you’re reading.

So if you want to rip this off specifically: your local store isn’t a cache, it’s the thing your UI actually renders from. The server’s job is to keep it in sync — not to sit between the user and their own data.

2. It lies to you, on purpose, and you love it

Hit send and your message appears instantly, marked as delivered, before the server has said a single word back. If it fails, the app quietly retries or flags it. But 99% of the time it just works, and you’re never made to sit there wondering if it went through.

Obvious once you say it out loud. Almost nobody actually does it, because it means the client has to juggle two versions of reality at once — what you think happened and what the server confirms happened — and merge them back together without the UI flickering when the real response shows up a beat later and (usually) just agrees with what you already assumed. Not showing a spinner is easy. Making that reconciliation invisible is the actual work.

Update local state the second the user acts, fire the network call after, and only make noise if it actually fails. The happy path shouldn’t be sitting through a round trip it never needed in the first place.

3. The wire protocol doesn’t make requests stand in line

Telegram runs its own transport, MTProto, instead of plain old REST over HTTPS. Skip the cryptography for a second — the part that matters for speed is that it’s built on a persistent connection where requests get pipelined. The client doesn’t send request A, sit around for the response, then send request B. It fires A, B, and C without waiting for any of them, and just deals with whatever comes back, in whatever order it lands.

It also uses a tight binary format instead of JSON, which is a smaller win than people assume — payload size rarely matters that much on a modern connection — but it does mean less parsing overhead on the device, which is a genuinely big deal when your users are on a five-year-old Android phone on spotty 3G in a country where every millisecond of CPU time is precious.

If your screen fires off four requests one after another purely because that’s how the code happened to get written, that’s free latency just sitting on the table. Multiplex them. Actually use the HTTP/2 or gRPC streaming you’re already paying for, instead of quietly behaving like it’s 2009.

4. Nobody let a generic UI framework anywhere near the hot path

This part’s less publicly documented, but it’s exactly what you’d expect from a team this obsessive: flat view hierarchies, hand-drawn list rows instead of five nested containers each with their own background and shadow, and a real vendetta against overdraw — the GPU wasting time painting the same pixel two or three times in one frame because your layout is a lasagna of opaque views stacked on top of each other.

Every mobile framework will happily let you build a chat bubble out of six nested containers. It’ll even work fine — right up until you scroll fast through a long list and the frame budget explodes, because the GPU is repainting pixels nobody’s even going to see. Fast apps get that way by doing more work up front, hand-drawing things, and asking the rendering system to do a lot less at runtime.

Worth actually profiling your paint and layout cost here, not just your API latency — a screen that fetches its data in 50ms but takes 200ms to lay out and draw is still going to feel sluggish, and no amount of protocol tuning fixes that.

What this looks like assembled

Strip out anything Telegram-specific and you’re left with a shape that works in any stack:

user does a thing
      │
      ▼
update local state instantly ───► UI updates (feels like 0ms)
      │
      ▼
queue a background sync
      │
      ▼
send it over a connection that doesn't make requests wait in line
      │
      ▼
server confirms or rejects
      │
      ▼
quietly reconcile — only make noise if it actually failed

The server-side stuff — datacenters scattered across the globe, routing you to the nearest one, CDN edges for media — matters too, and it’s a big part of why sending a video in Telegram doesn’t feel like uploading to 2011 Dropbox. But it’s also the least useful part of the playbook for most of us. You probably don’t need a fleet of regional datacenters to make your app feel fast. You need the client-side stuff above, and that’s a weekend project for one engineer, not a headcount request.

Okay but how do I actually try this without rewriting my whole app

You don’t need to invent your own binary protocol. Here’s the honest order of effort-to-payoff:

Optimistic-update your single most common action first. Whatever your users do most — send a message, add to cart, like a post — make it update instantly and sync in the background. This is usually the single biggest perceived-speed win on the entire list, and it’s a UI change, not an infrastructure one.

Make reads come from a local cache first. On mobile, a local SQLite/Room/Core Data store acting as your actual source of truth, synced quietly in the background, gets you most of what TDLib gives Telegram — without writing your own C++ core. On web, that’s a service worker plus IndexedDB, or a normalized client cache (React Query, Apollo, whatever) that renders from cache and revalidates after.

Stop firing requests one after another for no reason. If a screen needs four things, ask for all four at once. If you’re already on gRPC or HTTP/2, make sure you’re actually using the streaming/multiplexing you’re paying for instead of quietly behaving like an old REST client.

Measure your rendering cost before you touch your network code. Turn on GPU overdraw debugging on Android, or profile paint/layout time on web and iOS. A shocking number of “slow” apps are network-bound in theory and rendering-bound in practice. Optimizing the wrong bottleneck feels productive and changes nothing anyone notices.

Only build a custom binary protocol if you’ve genuinely run out of the above. It’s the most expensive line item here and gives you the smallest marginal return unless you’re operating at Telegram’s scale, across Telegram’s device zoo. Protobuf or MessagePack over a transport you already have gets you most of what MTProto’s serialization buys, for a fraction of the cost of designing and securing a protocol from scratch.

The part people leave out of “10x your app speed” posts

None of this is free. Optimistic UI means two sources of truth that have to agree with each other, and reconciliation bugs are a genuinely nasty category of bug — “it worked fine for me, the UI just flickered for this one specific user on this one specific connection” is a debugging session you’re signing up for. A cache-first client means cache invalidation, and yes, that’s the joke everyone makes about it being one of the two hard problems in computer science because it’s actually true. Hand-rolling your rendering layer means giving up a lot of the accessibility, testing, and tooling support you get for free from standard components.

Telegram pays these costs because messaging is the product — every millisecond of lag gets felt by every user, every single day, forever. If you’re shipping an internal dashboard nobody opens more than twice a week, most of this is overkill and you should go outside instead. If you’re building something people open dozens of times a day and feel every delay in — chat, feeds, anything with a compose-and-send loop — it’s worth the cost, all of it.

The actual takeaway isn’t “go copy Telegram’s stack.” It’s to steal the questions their engineers were clearly asking on repeat: does this really need to wait on the network? does this really need to happen one thing at a time? does this really need to be this expensive to draw? Ask those three questions about your own app’s critical path and you’ll almost always find at least one spot where the honest answer is “no, we just built it that way because it was easier.”

And okay, one more thing, purely for fun: the guy who actually designed MTProto is Nikolai Durov — Pavel Durov’s brother, and an honest-to-god math prodigy who racked up back-to-back gold medals at the International Mathematical Olympiad before he ever touched Telegram’s codebase. So, you know. Having a math genius for a brother probably didn’t hurt either. Can’t put that one in your sprint planning, unfortunately.

If you want to go straight to the source: Telegram’s technical FAQ and the TDLib repo on GitHub are both public and worth reading if you want to go deeper than a Medium post can take you.


메타데이터
post_id
83bda29e3e7b
slug
why-telegram-feels-so-freaking-fast-and-how-you-can-steal-its-tricks-83bda29e3e7b
url
https://medium.com/@kyryllupwork/why-telegram-feels-so-freaking-fast-and-how-you-can-steal-its-tricks-83bda29e3e7b
canonical_url
https://medium.com/@kyryllupwork/why-telegram-feels-so-freaking-fast-and-how-you-can-steal-its-tricks-83bda29e3e7b
author_url
https://medium.com/@kyryllupwork
status
ok
fetched_at
2026-07-21 04:28:33