Skip to main content

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:

  1. Represent money as integer minor units only.
  2. For IDR, use whole rupiah integer values (no decimal fractions).
  3. Avoid floating-point arithmetic in all payment and fee calculations.
  4. Use bigint where large intermediate calculations are possible.
  5. Enforce validation and constraints at API and database boundaries.
  6. 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 number math 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.
  • 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