The Rotor
One backend is a proxy; several backends is a load balancer.
What you're wiring up
Round-robin is the dumbest fair scheduler there is: 1, 2, 3, 1, 2, 3. You keep a counter, you increment it, you take it modulo the pool size. On paper it's one line of code.
The catch is concurrency. A hundred requests arrive in the same millisecond and all ask the rotor the same question at once: who's next? If two of them read the counter before either writes it back, they get the same answer and your perfect rotation quietly stops being perfect. Nothing crashes. The distribution just goes lopsided in production.
So this stage is really about shared mutable state: one integer, many goroutines, zero races. Do the increment atomically, or put it behind a mutex, and let the race detector be the judge. It names the exact two lines when you get it wrong.
Assembly steps
hint
Validate every URL at boot with url.Parse — dying at startup beats dying once per request.
hint
atomic.AddUint64 then modulo pool size is enough.
hint
A plain int with no lock will pass your manual testing and fail the race detector.
hint
log.Printf to stderr; keep stdout clean. You will lean on this log in every later stage.
Go deeper (after it passes)
Per-backend counts are your first real load measurement — that habit is DDIA M1 (Measure Before You Build). The counter discipline is Clean Code M10 (Clean Concurrency).