The Invoice — Episode 16
"One language for frontend and backend! Share code! One team! No context-switching!"
Splendid. Let us examine what you are actually paying for.
In 1995, Brendan Eich built JavaScript in ten days. For form validation and image rollovers. Thirty years later, it runs production backends. The language has not changed. The expectations have.
The Resource Invoice
Express: 20,000 requests per second. Go: 40,000. Rust: 60,000. On Lambda, Node.js averages 20ms. Rust: 1.12ms. Eighteen times faster for the same work.
The real cost is memory. Express idles at 30-50 MB. Go: 5-10 MB. Rust: 1-2 MB. V8's garbage collector runs whether you need it or not. When it pauses, your latency spikes. You are paying for the runtime, not your application.
The Architecture Invoice
One thread. That is your entire backend. Like a server in 1983. Every CPU-intensive operation blocks every other request.
The fix is not fixing the runtime. The fix is containerisation. Cannot use multiple cores? Spin up multiple containers. Cannot manage containers? Add Kubernetes. The entire cloud-native orchestration stack exists, in part, to compensate for a language that does not know what to do with your lovely CPU cores.
Go uses all cores by default. Rust uses all cores by default. Node.js uses one, then asks for an orchestration platform. One does wonder how we ended up here.
The Type Safety Invoice
JavaScript has no types. The fix: TypeScript, a transpilation layer that adds a mandatory build step to a language designed to run directly. Type checking consumes 95% of compilation time. A 22,000-line project takes 13 seconds. Large codebases: three minutes per compile.
The workaround: bypass tsc with swc (96% faster) and lose type checking at build time. You have added a type system, then disabled it for performance. Marvellous.
The Security Invoice
npm hosts 3.2 million packages. In 2024, 500,000 were malicious: 98.5% of all malware across every language ecosystem. In September 2025, eighteen packages with 2.6 billion weekly downloads were compromised in one attack. debug. chalk. In your dependency tree right now.
The insecurity is not merely npm. It is the architecture. Node.js loads third-party code at runtime with full system access. Go compiles dependencies once. Rust links statically. The attack surface is fundamentally different.
The Alternative
For I/O-bound real-time workloads (chat, streaming, webhooks), Node.js is genuinely competent. That is what it was designed for.
For everything else: Rust gives you a single binary with no garbage collector, native concurrency, and static linking at 1-2 MB. Go gets you halfway there, though Google's GC still takes its cut. Neither needs a transpilation step or a package manager at runtime.
The Energy Invoice
Pereira et al. (2021) measured energy consumption across languages, normalised to C = 1.00:
| Language | Energy | Memory |
|---|---|---|
| Rust | 1.03x | 1-2 MB idle |
| Go | 3.23x | 5-10 MB idle |
| JavaScript | 4.45x | 30-50 MB idle |
| PHP | 29.30x | 15-30 MB idle |
At data centre scale (1 billion requests/day):
| Language | Energy/year | Cost/year ($0.10/kWh) | CO2/year |
|---|---|---|---|
| Rust | ~45 MWh | $4,500 | 19.6 t |
| Node.js | ~195 MWh | $19,500 | 85.0 t |
| PHP | ~1,283 MWh | $128,300 | 559.4 t |
Your language choice is an infrastructure decision. And a climate decision.
Tenable rewrote one Node.js service in Rust: 700 CPUs saved, 300 GB RAM freed, 50% latency reduction.
The Pattern
Brendan Eich built JavaScript to validate forms. Ryan Dahl put it on the server, then called node_modules "an irreparable mistake" and built Deno to fix it. The language's own architect moved on. One does wonder whether the ecosystem noticed.
Read the full article on vivianvoss.net →
By Vivian Voss — System Architect & Software Developer. Follow me on LinkedIn for daily technical writing.

Top comments (0)