Overview
This forum post (originally published as an interactive Chinese-language dashboard on zhichai.net) presents a deep-dive analysis of billing-system architecture at Stripe-like scale, comparing a Rust stack (Axum + SQLx) against a Go stack (sqlc). The central thesis: for high-concurrency billing APIs, Rust's runtime query approach avoids the operational costs of Go's code-generation model.
*Note: This is a community technical analysis inspired by Stripe engineering practices, not an official Stripe engineering disclosure.*
The Scale Problem
The post frames billing systems as far beyond toy CRUD workloads. The stated challenges:
- 10M+ requests per second — unpredictable burst traffic and webhook floods
- <1ms latency targets — with strong consistency requirements
- Retry connection storms — failed retries can instantly overwhelm fragile connection pools
- Constant schema churn — metadata, indexes, and fields change daily
- No codegen — schema changes don't require rebuilding binaries; bottlenecks are handled at runtime, not compile time
- Async prepared statements —
pool.prepare_cached()automatically reuses query plans; SQL is validated at compile time with results extracted at runtime - JSONB + generated columns — leveraging Postgres features for zero-downtime schema evolution
- Weeks 1–2: SQLx async fundamentals — async prepared statements, pooled transactions, understanding Postgres behavior
- Weeks 3–4: Axum CRUD — build Invoices/Customers APIs, integrate Tower middleware (auth, rate limiting)
- Weeks 5–6: Schema evolution strategy — JSONB metadata extensions, generated columns, zero-downtime migrations without regeneration
- Weeks 7–8: Load testing and launch — wrk2 testing at 1M+ RPS, comparison against the legacy system (Fiber + sqlc), capstone: deploy a Stripe-style billing API on Fly.io
- Axum + SQLx billing API: 1.8M RPS, p99 < 1ms
- sqlc → SQLx migration: memory 4.2GB → 1.6GB, 3x throughput
The "Hidden Tax" of Go sqlc
While sqlc's type safety is pleasant at small scale, the author argues it becomes an operational burden under frequent schema changes. The pain cycle:
1. Schema change (add column / change index)
2. Run sqlc generate (code generation)
3. Full binary rebuild
4. Redeploy and restart (risk of service interruption)
Additional consequences cited: high struct-mapping overhead at high concurrency, increased goroutine scheduling pressure, and connection pools prone to collapse.
The Rust Alternative: Axum + SQLx
Reported Benchmarks (Billing API CRUD, 10K concurrency)
| Metric | Result | |---|---| | Throughput | ~3x higher for Rust vs Go | | P99 latency | -68% (0.6ms vs 1.9ms) | | Memory after migration | 4.2GB → 1.6GB | | Connection pool under 50K req/s bursts | Rust stack stays stable; Go setup exhibits thrashing | | Load testing tool | wrk2, up to 1M+ RPS |
Production Architecture (Layered)
1. Axum routes — /invoices, /customers
2. Tower middleware — auth, rate limiting, tracing
3. SQLx (async) — prepared statements, connection pooling
4. Postgres + JSONB — read replicas, PgBouncer
60-Day Migration Roadmap
Conclusion
The author argues that engineers proficient in this stack command high salaries not for syntax skills but for the ability to prevent real production incidents. Suggested résumé highlights from the migration: