What 'backend architecture' really means
Most framework debates conflate three independent layers: language runtime, HTTP framework, and deployment model. Separate them and clarity follows.
Layer decomposition: (a) language runtime — CPython with the GIL, V8 single-threaded event loop, Go's M:N scheduler, JVM with real OS threads. Each has fundamentally different concurrency primitives. (b) HTTP framework — Django, Express, FastAPI, Gin, Axum — provides routing, middleware, request parsing, response helpers. (c) deployment model — long-running process on a VM/container, FaaS (Lambda/Vercel Functions), edge runtime (Cloudflare Workers). Framework benchmarks almost never control for layers (b) and (c).
Concrete decompositions: 'Django on Gunicorn on ECS' = CPython + WSGI sync framework + pre-forked worker pool + long-running container. 'FastAPI on Uvicorn on Vercel' = CPython + ASGI async framework + event loop + serverless function. 'Next.js App Router on Vercel' = Node.js runtime + full-stack JS framework + mix of serverless and edge functions. Same ecosystem (Python), wildly different runtime/ops profiles.
Why benchmarks mislead: TechEmpower and similar compare framework overhead (μs per request) on synthetic workloads. Real bottlenecks are database queries (ms), external APIs (10s–100s ms), serialization of large JSON. Swapping a framework that's 2× faster on microbenchmarks rarely moves p95 latency — because your p95 is dominated by one slow Postgres query you haven't indexed.
What DOES matter between frameworks: (a) concurrency ceiling (can you serve 10k concurrent slow requests?), (b) ecosystem depth (ORM maturity, admin tooling, auth libs), (c) conventions that shape how your team writes code (opinionated vs composable), (d) operational properties (boot time, memory footprint, upgrade cadence).
Common conflation traps: (i) 'Python is slow' — conflates GIL-bound CPU work with I/O-bound work where Python is fine. (ii) 'Node is fast' — conflates event-loop I/O concurrency with CPU-bound work where Node is AWFUL (single-threaded). (iii) 'Go is simple' — simple language, but concurrency bugs (shared state across goroutines) are notoriously hard. (iv) 'Next.js is a backend framework' — it's a full-stack framework tightly coupled to its own UI; using it as a headless API for a mobile app wastes most of what makes it special.
Architectural vs code-level decisions: framework choice is architectural (hard to reverse, shapes hiring, affects everything). The sync-vs-async choice within a framework is also architectural. Whether you use class-based views vs function views inside Django is code-level. Identify which layer your decision lives at — architectural decisions deserve far more analysis than code-level ones.
Grounded on https://martinfowler.com/architecture/
Next up
Concurrency models — threads, events, goroutines
How a backend handles many simultaneous requests. Thread-per-request, event loop, goroutines, worker pools — each has radically different scaling ceilings.