When data flies from your service to a neighbouring one, the transport layer is responsible for delivery between two processes. And at that layer you have a choice of two protocols: TCP and UDP. They solve the same task — get bytes from sender to receiver — but with directly opposite philosophies. TCP is like a registered letter with a delivery receipt: slower, but it definitely arrives and in the right order. UDP is like a postcard dropped in a mailbox: gone instantly, but whether it arrives, and in what order, is nobody's promise.

Let's figure out how each one works and — most importantly — when a backend picks one over the other. Because the choice isn't about "which is better", it's about what matters more for a given task: reliability or speed.

TCP: delivery with a guarantee

TCP (Transmission Control Protocol) is built around the notion of a connection. Before sending a single byte of useful data, the two sides agree that they're ready to talk. From then on the protocol takes on a pile of chores to make sure the data arrives whole, without loss, and in the order it was sent.

What exactly TCP guarantees:

  • Delivery. If a packet gets lost on the way, TCP notices and sends it again. From your code's point of view the data just "arrived" — the retransmission mechanics are hidden inside.
  • Order. Packets can arrive out of sequence — via different paths, with different delays. TCP numbers them and reassembles them in the original order on the receiving side.
  • Integrity. Each chunk is checked with a checksum; corrupted data is discarded and requested again.

You pay for all this with latency and overhead: you need sequence numbers, acknowledgements, buffers. But for most backend tasks that's exactly what you want — you sent a request and you're sure it arrives just as it was.

Three-way handshake: how a connection opens

Setting up a TCP connection is a short exchange of three messages, called the three-way handshake. In plain terms it looks like getting acquainted over the phone:

  1. SYN — the client calls: "Hi, I want to talk to you, can you hear me?"
  2. SYN-ACK — the server answers: "I hear you. Can you hear me?"
  3. ACK — the client confirms: "I do. Let's start."

After these three steps both sides have confirmed the channel works in both directions and agreed on the starting numbers by which they'll reassemble packets. Only now does the real data flow.

Here hides a detail that matters for backend: the handshake costs one round-trip — one full "there and back" to the server before the first useful byte goes out. That round is called RTT (round-trip time). If the server is across an ocean, the RTT alone is tens of milliseconds, and they're added to every new connection before you've even sent a request. Hence the practical takeaway: connections are expensive to open, so they get reused — you don't open a new one per request, you keep a pool of ready ones. We cover this mechanic in the article on connections and their reuse.

Acknowledgements and retransmission

How does TCP know a packet arrived? By acknowledgements. After sending data, TCP waits for an ACK from the other side — "got it". If no ACK arrives within the allotted time, the protocol considers the packet lost and sends it again.

A rough analogy is talking over a walkie-talkie: you say a phrase and wait for "copy". No confirmation heard — you repeat it.

Sender               Receiver
   |-- packet 1 ------->|
   |<----- ACK 1 -------|
   |-- packet 2 ------->|   (lost!)
   |    ...waiting...   |
   |-- packet 2 (again)>|
   |<----- ACK 2 -------|

TCP also watches out not to swamp the network and the receiver with data: if packets start dropping, it slows down; on a calm line it speeds up. This is called congestion control, and it's exactly why the speed of a TCP connection isn't constant but adapts to the state of the network. What to do when a connection does drop or lag is the topic of the article on reliable transmission.

UDP: fast and without promises

UDP (User Datagram Protocol) is the exact opposite. No connection, no handshakes, no acknowledgements. Take the data, attach the receiver's port, toss it into the network — and forget it. Whether it arrived, in what order, or at all — the protocol doesn't check and doesn't report.

That sounds like a flaw, but it's precisely where UDP's strength lies:

  • Instant start. No handshake means no extra round-trip. The first data packet goes out right away.
  • Minimal overhead. The header is tiny, and no connection state is stored anywhere.
  • Freedom of behaviour. The application itself decides what to do about losses: ask again, substitute a placeholder, or just skip.

The price is that all the guarantees are now on your conscience. If a packet is lost, UDP won't find out. If two packets arrive in reverse order, that's how they'll land. So UDP is good where losing an individual chunk is either tolerable or something the application can handle on its own.

When to choose which

The rule is simple: every byte's integrity matters — TCP; speed and freshness matter more and losses are tolerable — UDP.

TCP is chosen when data can't be lost:

  • HTTP and the whole web — a page or API response must arrive whole, with no missing pieces.
  • Databases — losing half a query or response is unacceptable.
  • File transfer, payments, email — anything where every unit of data and its order matter.

UDP is chosen when being late is worse:

  • DNS queries — a tiny question and an equally tiny answer; if it's lost, we just ask again, which is cheaper than building a connection.
  • Video calls and voice — if one frame is lost, waiting for its retransmission is pointless: by the time it arrives, the conversation has already moved on. Better to skip it and show the next one.
  • Online games, metrics, telemetry — a stream of frequent updates where the fresh value matters, not each individual reading. A lost metric is covered by the next one a second later.

Notice the pattern: TCP is where the data is self-valuable; UDP is where the data quickly goes stale and a late packet is of no use to anyone.

QUIC: UDP with reliability on top

This picture has a modern sequel. For a long time the choice was hard: want guarantees — pay with TCP's handshake; want speed — take UDP and deal with losses yourself. QUIC breaks that dilemma.

QUIC is a protocol that runs on top of UDP but implements everything TCP is strong at itself: acknowledgements, retransmission of lost packets, ordering, encryption. The result is "UDP on the outside, reliability inside". Why do it this way? To get around limitations baked into TCP: for example, QUIC can establish a secure connection faster, in fewer round-trips, and doesn't stumble when one of several parallel data streams loses a packet.

HTTP/3 — the newest version of the protocol that the web is gradually moving to — is built exactly on QUIC. So UDP, which supposedly has "no guarantees", turned out to be the foundation for the most reliable and fastest HTTP available today. How HTTP changed from version to version is in the article on HTTP versions.

Where this applies

For a backend, the difference between TCP and UDP is first of all about where to look for the cause of a problem and why the code is arranged the way it is.

  • A connection pool is about the cost of the TCP handshake. When you set up a pool to a database or an HTTP client, you're paying to avoid doing a three-way handshake on every request. Once you understand that setup costs a round-trip, you understand why a pool exists at all.
  • "Slow on the first request, then fast" is often the handshake. The first call to a cold service includes setting up the connection (and with HTTPS, TLS on top of that); the following ones go over the already-open channel.
  • DNS "sometimes doesn't answer" is UDP's nature. A DNS query over UDP can be lost without any signal; the client simply repeats it on a timeout. If the timeout is large, the delay becomes noticeable.

Where beginners stumble:

  • They treat UDP as "broken TCP". UDP isn't worse — it's about something else. The absence of guarantees here isn't a bug but a deliberate trade for speed.
  • They think UDP is always faster. On a clean line, yes, but with losses the application still has to do something about the missing data, and naive retransmission on top of UDP easily ends up slower than TCP.
  • They open a new TCP connection per request. Each time you pay the handshake's round-trip. A connection pool exists precisely to avoid this.
  • They confuse a "connection" with an "application session". A TCP connection is a transport channel; a user session with tokens and cookies lives a layer above and has nothing to do with the handshake.

What to learn next

TCP and UDP are the transport, but they have neighbours across the layers worth understanding together. Below is addressing: IP addresses and ports, which determine exactly which machine and which process a TCP or UDP packet reaches. The application layer on top of TCP is DNS (which, by the way, lives on UDP) and HTTP versions, where QUIC takes the story up to HTTP/3. And to understand why connections are reused and what happens on drops, see the articles on connections and reliable transmission.