· go / typescript / bun

Go vs TypeScript — backend service language pick for 2026

Go for CPU-parallel workloads above 50k RPS. TypeScript on Bun for I/O-heavy APIs and full-stack teams. Team composition now drives the 2026 pick.

By Ethan

2,492 words · 13 min read

If you’re building a service where throughput ceiling matters — sustained 50k+ RPS, CPU-parallel workloads, or hundreds of thousands of concurrent goroutines — pick Go. If your team skews TypeScript already, your service is I/O-heavy, or you need to hire quickly from a broad pool, TypeScript on Bun is a genuine alternative now, not a fallback. The 2026 decision is no longer primarily about raw throughput. Bun has closed the gap to ~10–15% of Go on common HTTP benchmarks. The real decision axis is concurrency model and team composition.

Who this is for

Senior engineers and tech leads choosing a language for a new backend service in 2026. This is not a language tutorial. If you’re running an existing Go or TypeScript stack without a concrete performance or operational problem, the switch cost is almost certainly not worth it — stop here.

One thing to be explicit about upfront: Node.js is not the right comparison point in 2026. If your TypeScript stack runs on Node.js today, that’s fine — but when evaluating TypeScript as a Go alternative for a new service, Bun is what you should measure. The throughput gap between Go and Node.js is roughly 2×; the gap between Go and Bun is roughly 10–15%. These are different conversations.

Sources

All benchmark figures come from primary sources:

  • TechEmpower Framework Benchmarks Round 22 (November 2023) — Fiber v3/Go vs Express/Node.js on physical hardware
  • Bun v1.2 official benchmarks (February 2025) — HTTP/2 and Postgres client vs Node.js 22.13
  • Go vs Bun vs Node.js concurrent load benchmark — 100k concurrent request test (dev.to/hamzakhan, 2024)
  • Stack Overflow Developer Survey 2025 — professional usage figures
  • GitHub Octoverse 2025 — language growth data
  • JetBrains Go Ecosystem Report 2025 — Go adoption, framework usage, IDE data

Performance

The honest throughput picture at TechEmpower Round 22 with Fiber (Go, fasthttp-based) vs Express (Node.js):

TestFiber (Go)Express (Node.js)Factor
Plaintext11,987,976 RPS1,204,969 RPS~10×
JSON serialization2,363,294 RPS949,717 RPS~2.5×
Single DB query953,016 RPS441,543 RPS~2.2×
Multiple queries (20)54,002 RPS85,011 RPSNode.js wins
Data updates29,984 RPS54,887 RPSNode.js wins

Two things jump out. First, the ~10× plaintext gap is real but also the least useful number — no production service is running at plaintext throughput ceiling. Second, Node.js reverses the advantage on multiple-query and update tests. That’s not a language effect; it’s connection pool behavior. The DB round-trip dominates at that scale.

Now add Bun to the picture, from an independent 100k concurrent request benchmark:

RuntimeRPSMemory
Go~90,00055 MB
Bun~80,00060 MB
Node.js~45,000100 MB

Bun closes ~88% of the Go-vs-Node throughput gap. In simple HTTP handler benchmarks, Bun occasionally edges ahead of Go (the peterbe.com hello-world test reported Bun slightly faster, though he called it “an unrealistic scenario”). Bun v1.2’s HTTP/2 server runs 2× faster than Node.js 22 on H2 benchmarks; its Postgres client (Bun.sql) reads 50% more rows per second than the popular Node.js Postgres clients, using the binary wire protocol and native prepared statements.

What this means in practice: if your bottleneck is database latency — which it is for most CRUD services — Go and Bun are within measurement noise. Go’s advantage becomes real and measurable when you’re pushing past ~50k RPS of actual application load, or when CPU work per request is non-trivial (encoding, hashing, data transformation).

Memory footprint

At 100k concurrent requests, Go runs at 55 MB and Bun at 60 MB. Node.js lands at 100 MB — roughly 2× both.

The goroutine model explains Go’s behavior here. Each goroutine starts at ~2 KB of stack (the Go runtime grows it dynamically as needed). Running hundreds of thousands of goroutines in a single process is practical. In the Go FAQ: “It is practical to create hundreds of thousands of goroutines in the same address space.”

There’s one nuance worth knowing. In WebSocket benchmarks with 1 million mostly-idle connections, Node.js can consume less memory per connection than Go. The single event loop has negligible per-connection overhead when connections are idle. Goroutines shine when connections are actively computing; the event loop shines when connections are mostly sleeping.

Verdict on memory: Go and Bun are comparable under active load. Node.js uses roughly 2× more for request-heavy workloads. For idle-connection-heavy patterns (WebSocket servers where most clients sit quiet), measure before assuming.

Type safety

This is where the comparison gets interesting, because the answer depends on what “type safe” means to you.

Go uses nominal types with structural interfaces. type UserID string and string are not interchangeable — the compiler rejects the assignment. Any type implementing io.Reader satisfies the interface without declaration. Types are real at runtime; any carries type info via reflection. There’s no null safety at the language level, but Go replaces nullable with zero values and explicit error returns, which eliminates a large class of NPE-style bugs.

TypeScript uses structural typing everywhere. Two objects with the same shape are assignable to each other. The type system is more expressive than Go’s — conditional types, mapped types, template literal types, variance annotations, infer. TypeScript 5.7 added never-initialized variable detection and ships tsc 2.5× faster via V8 compile cache. The catch: types vanish at runtime. For input validation at system boundaries (HTTP request bodies, DB results), you need a runtime validator:

ToolBundle sizeUse case
Zod v4~12 KBComplex schemas, broad ecosystem
Valibot< 1 KBEdge functions, bundle-sensitive

Go doesn’t have this gap. Struct tags (json:"field") handle serialization safely without external libraries.

Verdict: Go wins on runtime type guarantees out of the box. TypeScript requires Zod or Valibot at system boundaries to match — that’s standard practice in mature TS projects, but it’s wiring you have to do. For internal code paths, TypeScript’s richer type algebra (conditional types, mapped types) expresses constraints that Go’s generics can’t match cleanly.

Ecosystem

For the specific needs of a backend service, both ecosystems are fully covered. The difference is breadth vs depth.

Go HTTP frameworks (JetBrains Go Ecosystem Report 2025):

  • Gin — 48% of Go developers; most popular, mature middleware story
  • chi — 12%; stdlib-compatible router, no magic
  • Echo — 16%; clean request binding
  • Fiber — 11%; fastest raw throughput (fasthttp), rising fast

TypeScript HTTP frameworks:

  • NestJS — enterprise-grade, Angular-style DI; the go-to for large teams wanting structure
  • Hono — ultra-lightweight; runs on Node, Bun, Deno, Cloudflare Workers without change
  • Elysia — Bun-native; fast, with end-to-end TypeScript inference
  • Express — still the tutorial-land default; 1.2M weekly downloads; 30 years of battle-testing

ORM / query layers:

Go is leaning toward code generation. sqlc compiles SQL queries to pure Go structs with no runtime reflection — it’s the option that closes the type-safety gap most tightly. Ent does graph-model code generation for complex schemas. GORM is the most popular but reflection-based. For raw Postgres performance, pgx bypasses the ORM entirely.

TypeScript ORM news in 2025: Prisma 7 replaced its Rust query engine with pure TypeScript, which eliminates the binary distribution step and meaningfully reduces cold-start and bundle size overhead (Prisma 7 release notes: “~90% smaller bundle sizes, up to 3x faster queries”). Drizzle (SQL-first, no query engine binary) is the better pick for serverless. TypeORM is aging.

gRPC: Go’s grpc-go is the reference implementation, maintained by the gRPC team at Google. TypeScript’s @grpc/grpc-js is a pure-JS port — solid, with 2,998+ npm dependents, but not the primary implementation.

stdlib advantage: Go’s standard library covers HTTP server, TLS, JSON, and cryptography at production quality. A Go service needs fewer third-party packages than an equivalent TypeScript service. TypeScript’s npm has the broader catalogue by far, but the Go stdlib means your dependency surface is smaller by default.

Hiring and onboarding

Stack Overflow Developer Survey 2025, professional usage:

LanguageProfessional usageYoY growth
TypeScript48.8%+2.5%
Go17.4%+2.0%

GitHub Octoverse 2025 named TypeScript the #1 language on GitHub in August 2025 with 2.6 million monthly contributors and +66.6% YoY growth. Go is in the top 10 but without a highlighted figure.

Go has 2.2 million professional developers using it as a primary language (JetBrains 2025) and 5+ million total. US salaries average $135,000/year, with seniors at $160k–$180k — a 15–20% premium over Node.js/TypeScript developers. Job postings grew 17% YoY. The catch: “there aren’t enough skilled developers to meet demand.” The Go talent gap is operational risk, not theoretical.

TypeScript developers with JavaScript backgrounds can be productive within weeks. Go requires learning explicit error handling, the goroutine model, and memory management philosophy — budget 1–3 months to production-level comfort.

Verdict on hiring: TypeScript has a ~3:1 advantage on available developer pool. If team scaling speed is a constraint, that’s real leverage. Go pays a salary premium and attracts strong engineers — but you may wait longer to fill roles and may need to grow Go engineers from TypeScript backgrounds rather than hire experienced ones directly.

Concurrency model

This is where the architectural decision is actually made.

Go’s model: M:N threading. Many goroutines multiplexed across OS threads by the Go scheduler (work-stealing, GOMAXPROCS-aware). Each goroutine starts at ~2 KB. Hundreds of thousands in a single process is practical. Go 1.23 improved time.Timer and time.Ticker for high-goroutine-count services. The failure mode: CGO calls block an OS thread; goroutine leaks are real and need pprof to find.

TypeScript’s model: Single-threaded event loop with cooperative async/await. Worker threads exist for CPU parallelism but coordination overhead is higher than goroutines. The failure mode is well-known: a 100 ms CPU spike blocks every concurrent request on that event loop. For I/O-bound services — database calls, external API calls, object storage — the event loop handles thousands of concurrent connections efficiently and you pay zero goroutine-scheduler overhead.

Bun extends the event loop model with JavaScriptCore instead of V8, HTTP/2 natively (without additional libs), and Bun.sql’s binary Postgres protocol. The concurrency ceiling is still the event loop, but Bun pushes that ceiling meaningfully higher than Node.js.

Pick Go when: you need parallel CPU work per request (image processing, crypto, data transformation), or you’re running services where goroutines independently schedule across cores matters (background job runners, high-concurrency data pipelines).

Pick TypeScript (Bun) when: nearly all time is spent waiting on DB/network, you want the npm ecosystem, or CPU-bound parallelism is rare enough that worker threads are sufficient.

Pick TypeScript (Node.js) when: Bun’s maturity is a risk for your specific use case (obscure native modules, strict LTS contract requirement) and performance is not a primary concern.

Verdict

AxisGoTypeScript + BunTypeScript + Node.js
Raw throughput (simple HTTP)HighestWithin 10–15% of Go~50% of Go
Memory under load55 MB / 100k req60 MB / 100k req100 MB / 100k req
CPU-parallel concurrencyGoroutines — scales to 100k+Event loop — breaks on CPU workSame as Bun
Runtime type safetyStrong (structs + reflect)Requires Zod/ValibotSame as Bun
EcosystemDeep stdlib + curated packagesnpm breadth + Bun-native libsnpm breadth
Developer pool17.4% professional usage48.8% combined48.8% combined
Hiring cost (US)$135k avg, 15–20% premium, talent gap$105k–$125k, large poolSame as Bun
gRPCReference implementationSolid port, not referenceSame as Bun
DeploySingle static binaryNode/Bun runtime requiredRuntime required
Onboarding new engineers1–3 months to fluencyWeeks for JS devsSame as Bun

Pick Go if: your service needs to sustain >50k RPS of actual application load, your workload includes meaningful CPU work per request, or operational simplicity of a single binary deploy matters to you.

Pick TypeScript + Bun if: your team already has TypeScript expertise, the service is I/O-bound (DB + API calls dominate), you need to hire quickly from a large pool, or you’re sharing types and business logic with a frontend.

Pick TypeScript + Node.js if: you need a strict LTS contract, your dependencies include C++ native addons that don’t yet run on Bun, or production battle-testing depth outweighs performance for your risk model.

Tooling note

Both Go and TypeScript have first-class editor support via LSP. If you’re picking an IDE that handles both without configuration, Cursor runs gopls for Go and tsserver for TypeScript out of the box — useful if your team works in both at once.

Caveats

  • Railway and Fly.io affiliate slugs don’t exist yet in toolchew. Both platforms support Go single-binary and Bun natively and would be natural callouts in a deployment section. Filed for a row insert in mmo.affiliate_links.
  • TechEmpower Round 22 is from November 2023. Round 25 (Q1 2026) reportedly includes Bun with ~1M plaintext RPS/core (~2.5× Express, ~0.33× Go Gin). When Round 25 data is publicly confirmed, the performance section should be updated with the newer figures.
  • The 100k concurrent request benchmark (dev.to/hamzakhan) is a single-author benchmark on specific hardware. Treat the absolute RPS numbers as directional, not production-verified. The relative ordering (Go > Bun > Node.js) is consistent with multiple independent sources.
  • Go salary and job market data is from Signify Technology’s 2025 Go job market analysis — a single recruiter’s dataset. Cross-reference with Stack Overflow salary data before using this in compensation decisions.
  • Bun compatibility with Node.js native addons is broad but not 100%. If your stack includes C++ addons, verify before committing to Bun.

References