ADR-011: Backend Language and Money Precision Strategy
Status: Accepted
Date: 2026-03-08
Context
We considered moving the backend from TypeScript to Go due to concerns about payment correctness, especially floating-point precision issues in JavaScript/TypeScript.
The current architecture is already integrated with:
- Next.js + OpenNext on Cloudflare Workers
- Hono API routes in the same Worker deployment
- Existing TypeScript domain and API scaffolding
A language migration would add significant delivery risk and complexity for deployment/runtime integration without directly solving precision unless the money model is corrected.
Decision
We will keep the backend in TypeScript and adopt a strict money model:
- Represent money as integer minor units only.
- For IDR, use whole rupiah integer values (no decimal fractions).
- Avoid floating-point arithmetic in all payment and fee calculations.
- Use
bigintwhere large intermediate calculations are possible. - Enforce validation and constraints at API and database boundaries.
- Centralize money operations in dedicated utility/domain functions with tests.
Rationale
- Precision problems are caused by data representation and arithmetic choices, not by TypeScript itself.
- Integer-based money handling is a standard and reliable approach in payment systems.
- This approach preserves current Cloudflare Workers architecture and delivery speed.
- Risk and cost are lower than a backend language migration.
Consequences
Positive
- Avoids floating-point rounding errors in payment logic.
- No major rewrite or platform migration required.
- Keeps backend and frontend operational model consistent.
- Faster path to production for Xendit integration.
Negative
- Requires strict engineering discipline (no ad-hoc
numbermath for money). - Need additional guardrails (lint rules, utility wrappers, tests, DB constraints).
Implementation Notes
- API contracts should accept/return amounts as validated integers (or strings when crossing systems that may lose precision).
- Database amount columns should be integer types with non-negative checks.
- Payment service code must use centralized money helpers for arithmetic and rounding.
- Add unit tests for all money transformations and edge cases.
Alternatives Considered
Move Backend to Go
- Pros: Stronger type semantics around numeric handling, team familiarity in some ecosystems.
- Cons: Significant migration effort, added runtime/deployment complexity on Cloudflare stack, does not automatically prevent precision bugs without integer money modeling.
Keep TypeScript with Floating Numbers
- Pros: Minimal immediate code changes.
- Cons: Unacceptable precision risk for payment operations.
Related
- ADR-003:
docs/adr/ADR-003-payment-integration.md - RFC-001:
docs/rfc/RFC-001-payment-integration.md - ADR-010:
docs/adr/ADR-010-database-d1.md