5 min read
Python is for prototypes, Go is for scale. Choosing your backend stack in 2026 comes down to one boring question: where on the lifecycle are you, and how much pain are you willing to absorb later? Stop picking by vibes, stop picking by what your mate at Monzo tweets about, and stop rewriting your prototype in Go three months in because you read one Hacker News thread. I have shipped Python services that quietly serve 40k requests a minute for the BBC, and Go services that died under the same load because someone thought channels were free. The language is the smallest variable. The team, the deploy story, and the data model are the actual decisions.
Python's home turf is fast iteration, weird data, and glue. FastAPI on 3.12 with Pydantic v2, an async ORM like SQLAlchemy 2.0, and a Celery worker for the slow bits will get you from zero to a working internal tool in a weekend. HMRC's open-source repos are full of this pattern: a thin FastAPI front, a Postgres back, and a Python script that nobody is allowed to touch because it works. None of that is a weakness. It is the job.
The trap is treating that weekend build as production. Async Python is not magic. A single GIL-blocking call inside an async handler will stall the event loop, and your shiny async endpoint will run single-threaded while you blame the framework. Gevent workers, thread pools, and process managers are bandages. If your service has to hold 5,000 concurrent websockets open on one box, you picked the wrong language. Python's ceiling is real, and it is lower than Go's. That is fine. Just don't pretend otherwise.
CPU-bound fan-out. Image pipelines, PDF generation at volume, anything that needs to chew JSON for minutes. NumPy helps, but the moment you need to parallelise the rest of the request, you are forking workers and praying. NHS Digital learned this with their screening pipelines: the Python orchestrator calls out to a Go worker, because Go is the right tool for that bit. Use Python where the bottleneck is I/O, your ORM, or human speed. Use it where the next bottleneck is your own decision-making.
Go is for services that have to stay up under load, on a small box, with a small on-call rotation. One statically linked binary, no virtualenv, no "works on my machine", deploys in seconds. The standard library net/http is genuinely enough for most APIs. Go 1.23 with the new range-over-func iterators has killed the last excuse for ugly generics code in production.
Concurrency is cheap. A goroutine costs roughly 2KB of stack. You can spin up 100k of them and the scheduler will keep up. Memory is predictable, p99 is predictable, and the binary you shipped in 2023 still runs on the same image in 2026. Compare that to a Python service whose deps need a careful lockfile ritual and a base image rebuilt every six months. If your team is two engineers in a regulated environment running GOV.UK-style services, that predictability is worth real money.
Where Go loses is iteration speed. Writing a CLI in Go takes an afternoon. Writing the same CLI in Python takes twenty minutes. Glue code with weird third-party APIs is miserable in Go because the type system forces you to model the whole thing before you can call it. So Go wins the steady-state services, loses the one-off scripts.
Here is the rule I actually use on UK teams:
Notice none of those bullets mention "rewrite for performance." Most rewrites are a lie we tell ourselves to look productive. If your Python service is slow, profile it. Ninety percent of the time the fix is adding an index, batching the ORM calls, or moving one slow job off the request path. The other ten percent is when you actually need Go, and you should be honest about which bucket you are in.
Fanboy benchmarks are useless. Here is a minimal one you can run on your laptop in 2026, both languages, same machine, no tricks:
// server.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "ok")
})
http.ListenAndServe(":8080", nil)
}
# app.py
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def root():
return "ok"
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080, workers=4)
Run each, then hit it with hey -z 30s -c 200 http://localhost:8080/. On a 2024 M-series Mac you'll see Go doing roughly 6x the requests per second at one-fifth the memory. That is not because Go is "faster", it is because the runtime is doing less per request. For a real workload with a database and JSON marshalling, the gap shrinks. The point is: if you genuinely need that gap, you know. If you don't, you don't.
The worst UK engineering decisions I have seen in the last two years were "we are rewriting our Python billing system in Go because scale". The team had 200 paying customers. The rewrite took eight months. The new Go service had worse correctness than the old Python one because they lost domain knowledge in the move. The CTO got a promotion for "modernisation". The company nearly went under.
Languages are tools, not identities. Pick Python where iteration matters, Go where steady-state matters, and don't let anyone on the team mistake a language choice for a technical achievement. Your job in 2026 is not to pick the shiniest stack. It is to pick the one that your team can actually run at 3am when the alerts go off.
Yes, for prototyping, data pipelines, internal tooling, and any service where iteration speed beats raw throughput. FastAPI and Django are both healthy. The mistake is using Python for services that need to hold tens of thousands of concurrent connections per box.
Only if you have a measured bottleneck that Python cannot fix at the architecture level. If your p99 is bad because of N+1 queries or missing indexes, Go will not save you. If your bottleneck is the GIL or memory per connection, then yes, Go is the right answer.
Node sits in a similar niche to Python but with a worse ecosystem for typed backends. Rust is a legitimate third option if your team already knows it, but the hiring market in the UK is thinner than Go. Elixir is excellent for specific workloads like chat or telephony. For a generic web backend in 2026, Python or Go cover 95% of cases.