The Scales
Round-robin is blind: it hands a struggling server the same share as a healthy one.
What you're wiring up
A backend that's alive but slow is the nastiest failure mode, because health checks say it's fine. Round-robin keeps feeding it a third of your traffic and a third of your users wait. Least-connections fixes this without measuring latency at all: send each request to whoever is currently doing the least work. Count requests in flight per backend, pick the minimum, done. A slow backend accumulates in-flight requests and naturally stops being chosen.
The bookkeeping is where this stage bites. Increment when you dispatch, decrement when the response finishes — on every path, including errors, timeouts and panics. That's what defer is for. A counter that leaks on the error path silently pins all your traffic to one backend forever.
There's a design lesson riding along. Round-robin and least-connections are two interchangeable answers to one question: who's next? Pull that question behind an interface and you've arrived at the Strategy pattern because the code demanded it, not because a book told you to.
Assembly steps
hint
This is a pure refactor — run the Stage 3 checks straight after as your own regression gate.
hint
atomic.AddInt64(&b.inflight, 1) with the matching defer right where the request is handed to the transport.
hint
If the increment and the defer live in different functions, you'll eventually leak one.
hint
Deterministic tie-breaking is what makes the behavior testable — don't reach for randomness.
Go deeper (after it passes)
The Picker interface is HFDP M1 (Ducks That Swap Their Wings, Strategy) arrived at by need. The counter discipline under load is Clean Code M10 again.