TCP vs UDP, Explained Through What Breaks When You Pick Wrong
TCP and UDP aren't interchangeable. We walk through the exact failure modes — head-of-line blocking, silent packet loss, Nagle delays — that show up when you pick the wrong transport.
Most “TCP vs UDP” explanations stop at a feature table: TCP is reliable and ordered, UDP is fast and connectionless. True, and useless. You don’t feel the difference until a wrong choice ships and something behaves in a way the table never warned you about — a multiplayer game that stutters precisely when the network is busiest, a metrics agent that reports numbers from 90 seconds ago, a file transfer that arrives corrupted with no error logged anywhere.
The useful way to learn the two protocols is backwards: pick each one for the wrong job and watch what breaks. The failure modes are specific, repeatable, and they map directly onto the guarantees each protocol makes.
What TCP guarantees, and what those guarantees cost
TCP gives you a byte stream that arrives in order, with no gaps and no duplicates, or the connection dies trying. To deliver that, it opens with a three-way handshake (SYN, SYN-ACK, ACK), assigns every byte a sequence number, acknowledges what it receives, retransmits what it doesn’t, and slows itself down when the network signals congestion. You write send(), the bytes come out the other end in the right order. That contract is why HTTP, SSH, and database wire protocols all sit on top of it.
The cost is hidden in the word ordered. TCP will not hand your application byte 5,000 until bytes 1 through 4,999 have arrived. If a single packet in the middle is lost, every packet that arrived after it sits in the kernel’s receive buffer, complete and useless, until the retransmission of the missing one lands. This is head-of-line blocking, and it is the single most important TCP behavior nobody mentions in the feature table.
Now pick TCP for a 60-tick multiplayer shooter. Each tick you send a position update. A packet drops — normal on any real network. TCP detects the loss and retransmits, which on a typical link takes at least one round-trip time, often more once the retransmission timer is involved. For that entire window, every newer position update is stuck behind the lost one. The player freezes, then snaps forward when the backlog flushes. The cruel part: this gets worse under load, exactly when players notice. You picked the protocol that prioritizes delivering stale data over delivering fresh data, in a domain where stale data is worthless.
There’s a quieter TCP trap too: Nagle’s algorithm. To avoid flooding the network with tiny packets, TCP may hold a small write, waiting to coalesce it with the next one. Combined with delayed ACKs on the receiver, this can stall a small request-response exchange for up to roughly 40 ms while each side waits for the other. For a chatty protocol sending many small messages, that latency is invisible in a LAN test and brutal in production. The fix is TCP_NODELAY, but you only reach for it once you know the behavior exists.
Where UDP wins, and the bill it hands you
UDP is almost nothing: a 8-byte header, source and destination ports, length, checksum. No handshake, no sequence numbers, no acknowledgments, no retransmission, no ordering, no congestion control. You hand the kernel a datagram and it tries once. The datagram arrives intact, arrives corrupted-and-discarded, arrives out of order relative to its siblings, arrives duplicated, or never arrives — and UDP tells you nothing about which happened.
That sounds worse, until you remember the game. With UDP, a lost position update is simply skipped; the next datagram carries a newer position anyway, so there’s nothing worth retransmitting. No head-of-line blocking, because there is no line. This is why real-time voice, video, and games live on UDP, and why QUIC — the transport under HTTP/3 — was built on UDP specifically to escape TCP’s head-of-line blocking while rebuilding reliability per-stream.
But UDP hands you a bill, and developers underpay it constantly. Pick UDP for a job that actually needs reliability — say, shipping log lines to a collector — and you will reinvent TCP, badly. First you notice lines go missing under load, so you add acknowledgments. Then duplicates appear, so you add sequence numbers to dedupe. Then you discover messages arrive out of order, so you add a reordering buffer. Then the receiver gets overwhelmed because nothing throttles the sender, so you add flow control. You have now written a worse TCP, with more bugs, and you still don’t have congestion control, so your agent contributes to network collapse during an incident.
There’s also a size trap. A UDP datagram larger than the path MTU (commonly around 1500 bytes on Ethernet) gets fragmented at the IP layer. If any single fragment is lost, the entire datagram is discarded — and many middleboxes drop IP fragments outright. So a 4 KB UDP message can vanish on networks where a 1 KB one always works, with nothing in your logs. Keeping datagrams under the MTU is a constraint you have to enforce yourself.
The decision, framed by failure mode
Skip the feature checklist. Ask one question: when a packet is lost, what does your application want to happen?
If the answer is “wait for it, I need every byte in order” — file transfer, an API call, a database query, anything where a gap corrupts meaning — use TCP and accept the latency variance. If the answer is “skip it, the next one supersedes it” — live telemetry, game state, voice, anything where freshness beats completeness — use UDP and budget engineering time for the reliability you do need.
| Failure mode | TCP | UDP |
|---|---|---|
| Packet lost mid-stream | Retransmits; later data blocks until it arrives | Skipped; next datagram proceeds independently |
| Out-of-order arrival | Reassembled in order for you | Delivered as-is; you reorder if you care |
| Receiver overwhelmed | Flow + congestion control throttle the sender | Nothing throttles; you build it or melt the link |
| Message exceeds MTU | Segmented transparently | IP-fragmented; one lost fragment drops the whole message |
The trap on both sides is the same shape: each protocol’s strength is the other’s failure mode. TCP’s ordering becomes head-of-line blocking. UDP’s leanness becomes a pile of reliability code you have to write and test yourself. Picking well means knowing which failure your application can tolerate, not which feature list looks longer.
Cursor
Socket bugs hide in the gap between what the protocol guarantees and what you assumed. An AI-native editor that reads your whole networking layer helps catch the mismatches — a UDP path that assumes ordering, a TCP write that needs TCP_NODELAY — before they ship.
Free tier; Pro at $20/mo
Affiliate link · We earn a commission at no cost to you.
FAQ
Is UDP always faster than TCP?
Why does HTTP/3 use UDP if UDP is unreliable?
Can I just use TCP everywhere and add TCP_NODELAY to fix latency?
The protocols haven’t changed in decades. What changes is whether you chose the one whose failure mode your application can actually absorb.
Related reading
2026-06-22
Write-Ahead Logging: How Databases Survive a Power Cut
How write-ahead logging keeps your data intact when the machine dies mid-write — the log-first rule, fsync, checkpoints, and why PostgreSQL and SQLite both rely on it.
2026-06-22
Backpressure, Explained Through a Queue That Won't Fall Over
What backpressure actually is, why an unbounded queue is a memory leak in disguise, and the four strategies a producer can take when a consumer falls behind.
2026-06-22
What a Bloom Filter Actually Saves You (and When It Lies)
A bloom filter trades a small false-positive rate for big memory savings. Here is the math behind the trade, where it pays off, and the failure mode that bites people.
2026-06-22
Idempotency, Explained Through the Retry That Doesn't Double-Charge
A practical look at idempotency keys: why a retried payment request shouldn't charge a card twice, how the pattern works, and where it quietly breaks in production.
2026-06-12
Git Plumbing in Practice: How CI, Review Tools, and AI Agents Build on Git's Primitives
How CI runners, stacked-diff CLIs, code review systems, and AI coding agents build on Git's object model — blobs, trees, commits, and refs — instead of reinventing version control, and how to start building on the plumbing yourself.
Get the best tools, weekly
One email every Friday. No spam, unsubscribe anytime.