Status: Accepted · Date: 2025-Q1 · Deciders: Aarokya Engineering
Context
We needed a backend that could:- Handle concurrent payment webhook processing without data races
- Be deployed on constrained infrastructure (cost matters for a gig-worker product)
- Integrate with an existing Rust/Smithy expertise within the team
- Give us compile-time guarantees on SQL queries and API contract shapes
Language Decision: Rust
Why not Node.js / TypeScript?
Why not Node.js / TypeScript?
Node is the natural choice given the TypeScript frontend. However:
- Payment processing logic benefits from strong type safety and no null/undefined surprises
- The GIL-equivalent in Node’s event loop limits true CPU parallelism for compute-heavy operations
- Memory safety guarantees matter when handling financial data
- Team had deeper Rust expertise for this domain
Why not Go?
Why not Go?
Go is a strong contender with excellent concurrency primitives, fast compile times, and a mature ecosystem.
- Go would have been a reasonable choice
- Rust was preferred for the compile-time safety guarantees on DB queries (Diesel) and API contracts (smithy-rs)
- The team’s existing Rust proficiency tipped the decision
Rust
Rust
- Memory safety without GC — no runtime crashes from null dereferences or data races
- Smithy-rs generates production-quality code (it’s what AWS uses internally)
- Diesel provides compile-time verified SQL queries
- Excellent async story via Tokio (used by both Actix-web and our async worker)
- Small binary, low memory footprint — cost-efficient deployment
Framework Decision: Actix-web
| Framework | Why Considered | Why Not Chosen |
|---|---|---|
| Axum | Type-safe routing, Tokio-native, rapidly growing | We evaluated Actix-web first; no compelling migration reason mid-project |
| Rocket | Ergonomic macros, good documentation | Historically had blocking issues in async contexts |
| Actix-web | Battle-tested, excellent performance, utoipa integration, mature ecosystem | — |
ORM Decision: Diesel
Why Diesel over SQLx:- Diesel’s query builder is checked at compile time — a wrong column name or type mismatch is a compiler error, not a runtime panic
- For financial data (wallet balances, payment orders), this level of safety is worth the synchronous API trade-off
- SQLx’s async advantage is partially mitigated by using
async-bb8-dieselfor the connection pool
Architecture: How the Crates Fit Together
Consequences
Gained
- Compile-time SQL safety (Diesel)
- Compile-time API contract safety (smithy-rs)
- Memory-safe concurrent payment processing
- Low-memory production footprint
- Strong ecosystem for background job patterns (Tokio tasks)
Trade-offs accepted
- Diesel is synchronous — mitigated with
async-bb8-dieselpool - Rust compile times are longer than Go/Node
- Steeper onboarding curve for new engineers
- Schema migrations require Diesel CLI (
diesel migration run)