πŸ” UnitySwitch Backend Developer Constitution

Lead Backend Dev: Chimuka Mukwenya

Strictness Level: Bank-Grade (Non-Negotiable)

Goal: Zero financial loss due to code defects. Full BOZ (Bank of Zambia) audit compliance.


Rule 1: The "Append-Only" Ledger Law (Database)

Rule: You are forbidden from using UPDATE or DELETE SQL statements on the transactions or ledger_entries tables.

Rule 2: The Idempotency Mandate (Double-Payment Guard)

Rule: Every single payment endpoint must accept an X-Idempotency-Key header from the mobile app.

Rule 3: The "Two-Phase" Validation Rule (Balances & Race Conditions)

Rule: Always validate the sender's balance twice within a database transaction.

tx := db.Begin()
var balance int64
tx.Raw("SELECT balance FROM wallets WHERE user_id = ? FOR UPDATE", userID).Scan(&balance)

if balance < (amount + fee) {
    tx.Rollback()
    return errors.New("insufficient_balance")
}

tx.Exec("UPDATE wallets SET balance = balance - ? WHERE user_id = ?", (amount+fee), userID)
tx.Commit()

Rule 4: Timeout + Real Circuit Breaker (NFS Adapter) β€” Enhanced

Rule: Every call to the NFS or external banks must have a strict, hard-coded timeout of 3 seconds, and all calls must pass through an actual circuit breaker β€” not a timeout alone.

import "github.com/sony/gobreaker"

var nfsBreaker = gobreaker.NewCircuitBreaker(gobreaker.Settings{
    Name:        "nfs-adapter",
    MaxRequests: 3,
    Interval:    60 * time.Second,
    Timeout:     30 * time.Second, // how long breaker stays OPEN before half-open
    ReadyToTrip: func(counts gobreaker.Counts) bool {
        return counts.ConsecutiveFailures > 5
    },
})

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
resp, err := nfsBreaker.Execute(func() (interface{}, error) {
    return sharedHTTPClient.Do(req.WithContext(ctx))
})

Rule 5: The "Trace ID" Law (Observability)

Rule: Every single log, API error, and database record must include a request_id (Trace ID).

Rule 6: The "Secrets" Rule (Zero Exposure)

Rule: You will never, ever, EVER commit a password, API key, or bank TLS certificate to GitHub.

Rule 7: The "Bank-Grade" Logging Standard

Rule: Structured JSON logs only. fmt.Println is strictly prohibited.

// BAD
fmt.Println("NFS failed for user 123")

// GOOD
logger.Error().
    Str("request_id", reqID).
    Str("user_nrc", nrc).
    Str("error_type", "NFS_TIMEOUT").
    Int("amount", 100).
    Msg("External transfer failed")

Rule 8: The "Decoupled" Rule (No App-to-DB Direct Access)

Rule: The Flutter mobile app cannot talk to the Database directly.

Rule 9: Money Is Always an Integer, Never a Float

Rule: All monetary values are stored and computed as int64 representing the smallest currency unit (ngwee β€” 1 ZMW = 100 ngwee). float32/float64/unconstrained NUMERIC are forbidden for any amount, balance, or fee field.

type Transaction struct {
    AmountNgwee int64 // never float64
    FeeNgwee    int64
}

Database columns: BIGINT, not FLOAT/REAL/DOUBLE PRECISION.

Rule 10: A Timeout Is Not a Failure β€” Ambiguous NFS Outcomes Must Go to PENDING

Rule: Any NFS call that times out, errors at the network level, or returns an ambiguous response must set the transaction status to PENDING_VERIFICATION β€” never FAILED, and never auto-retried as a brand-new transaction.

if err != nil {
    tx.Exec("UPDATE transactions SET status = 'PENDING_VERIFICATION' WHERE id = ?", txnID)
    // do NOT return "failed" to the user yet β€” queue a status check
    enqueueStatusCheck(txnID)
    return
}

Rule 11: Mandatory Settlement Reconciliation

Rule: A scheduled job must compare every PENDING/SUCCESS ledger entry against the corresponding NFS settlement report and flag mismatches.

Rule 12: Maker-Checker for Manual Money Movement

Rule: Any admin-initiated refund, ledger correction, or account freeze/unfreeze requires two distinct authenticated users β€” one to propose, one to approve.

Rule 13: AML/KYC Monitoring, Decoupled From the Payment Path

Rule: Every transaction is published asynchronously to a monitoring pipeline that checks velocity/threshold rules and flags anomalies β€” independent of whether the payment itself succeeds.

Rule 14: PII Lives Outside the Immutable Ledger

Rule: transactions/ledger_entries store a tokenized reference ID, never raw PII (NRC number, phone number, full name) directly.

Rule 15: Tested, Point-in-Time Recoverable Backups

Rule: Continuous WAL archiving / point-in-time recovery on Postgres, with a documented RPO/RTO and a quarterly restore drill into a sandbox.

Rule 16: Every Inbound NFS/Bank Callback Must Be Signature-Verified

Rule: Any asynchronous settlement/confirmation callback from NFS or a partner bank must be signature-verified (HMAC with shared secret, or mTLS) before its payload is trusted.

Rule 17: API Authentication & Authorization (Non-Webhook Endpoints)

Rule: All client-facing and internal endpoints (except inbound callbacks) must enforce strong authentication. No endpoint may default to anonymous access.

Rule 18: Rate Limiting & Brute-Force Protection

Rule: Every payment-related and authentication endpoint must enforce strict per-user (and per-IP) rate limits, configured to fail closed.

Rule 19: Data Encryption at Rest

Rule: All databases, file stores, and backups containing financial data, PII, or configuration must be encrypted at rest using AES-256 or stronger, with keys managed exclusively through a cloud KMS or Hardware Security Module (HSM).

Rule 20: Proactive Monitoring & Alerting (SLIs/SLOs)

Rule: The service must expose core Service Level Indicators (SLIs) as Prometheus metrics and trigger PagerDuty/Opsgenie alerts on defined thresholds.

Rule 21: Performance & Load Testing Gates

Rule: Before any production release, a load test must prove that the payment endpoint sustains at least 2Γ— the projected peak transactions per second (TPS) with p99 latency under 200ms (excluding the NFS call itself). Results are archived and compared; no deployment if performance degrades >10%.

Rule 22: Immutable Audit Trail for Config & Admin Changes

Rule: Any change to system configuration, user roles, fee structures, blacklists, or NFS routing must be logged to an append-only admin_audit_log table. This table follows the same rules as the ledger: INSERT and SELECT only, no UPDATE or DELETE.

Rule 23: Mandatory BOZ Regulatory Reporting

Rule: A scheduled process must generate and securely archive daily regulatory reports required by the Bank of Zambia, including:


🚨 CI/CD & PR Enforcement (Lead Dev's Toolbox)

  1. Branch Protection: main/master is locked. Require Pull Request, no direct commits. Require 2 approvals.
  2. Pipeline Gates (GitHub Actions / GitLab CI):
    • Lint: golangci-lint (catches fmt.Println, float64 on money fields, bad practices).
    • Tests: 100% coverage on payment orchestration logic (go test -race ./...).
    • Security Scan: gosec for hardcoded credentials and insecure HTTP usage.
    • Reconciliation Smoke Test: Reconciliation job runs against mock NFS settlement files in staging.
    • Load Test Gate: Performance test passes (no >10% regression).
    • Compliance Check: Encrypted storage verification, metrics endpoint check, report generation dry-run.
  3. The Golden Review Rule: If a PR uses UPDATE on the ledger, stores raw PII on a ledger table, sets status = 'FAILED' directly on an NFS timeout, bypasses the breaker-wrapped HTTP client, allows self-approval on a manual intervention, lacks authentication middleware, omits rate limiting, adds unencrypted storage, or skips audit logging β€” block the merge. Protecting the money and the BOZ license is everyone's #1 priority.
Lead Dev's Final Word: The goal of this constitution is not to slow us down. It's to prevent a single bug from destroying our reputation β€” or our license. If the code passes these 23 rules, deploy with confidence.

PR Template Checklist