Skip to content
Codedock
ServicesHow we workInsightsCase StudiesCareerContact
Back to all articles
Enterprise Integration

·

7 min read

·

Written by Tomáš Mikeš

MLM commission calculations: how to model them so they survive an audit

For JUST we replaced a legacy system with complex commissions. Recalculation history, versioned rules, reconstructing a computation years later — here's how to approach it so it survives the first auditor.

MLMCommissionsAuditBusiness Logic

Multi-level marketing commissions are a discipline of their own. Seller A recruits seller B, B does 50 000 CZK/month in sales. A earns commission on B's volume — how much depends on A's tier, which qualification A held that month, whether A hit the bonus threshold, and whether A's override bonus on sellers C and D (both under B) was unlocked.

For JUST we replaced a system that had been doing these calculations for 15 years. Rule #1 when replicating: the new system must return the same number if you run both side by side on the same data. Rule #2: three years from now you must be able to explain why a seller got 3 240 CZK instead of 3 470 CZK.

Here's how we structured it.

1. Commission rules as data, not code

A developer's instinct: “I'll write calculateCommission(seller, month) with if/else on tiers.” Works for 6 months. Then business lands a new bonus and you amend the function. Then a change, then another. Three years in you have 6 versions of the function and you can't separate them by the month they applied.

The right way: rules as rows in a DB table with validFrom / validTo. Schema:

CommissionRule {
  id: UUID
  name: 'Tier 2 Base Commission'
  validFrom: Date
  validTo: Date | null
  condition: JSON   // when it applies
  calculation: JSON // what is computed
  priority: Integer
}

Computing for month 2023-05 loads rules where validFrom <= '2023-05-01' AND (validTo IS NULL OR validTo > '2023-05-31'). A business logic change = a new row, not a code edit. Historical periods are recomputed with the rules that applied at the time.

2. Calculation outcomes are immutable

Once a commission is calculated and the seller is paid, it is not rewritten. Even if a bug in the rules is found. Instead of overwrites, a correction is recorded.

Schema:

CommissionOutcome {
  id: UUID
  sellerId
  month: '2023-05'
  amount: 3240
  ruleSnapshot: JSON  // every rule used, as copy
  inputSnapshot: JSON // sales volumes, network at calc time
  calculatedAt: DateTime
  status: 'final' | 'corrected' | 'superseded'
}

CommissionCorrection {
  id: UUID
  originalOutcomeId
  correctionAmount: 230  // +/-
  reason: string
  approvedBy: UserId
  approvedAt: DateTime
}

Payout to the seller = original + all corrections. The financial system has an immutable audit trail. In 2026 when an auditor asks “why was this 2023-05 commission 3 240 CZK?” you can replicate it — the snapshot of rules and inputs is there forever.

3. Test scenarios as a business-engineering contract

Business says: “seller Jana has volume 80 000, under her is B with 60 000, under B is C with 40 000, Jana gets 3% override on B and 1.5% on C. Compute.” We run it, get a number, business confirms “correct.” That must be stored as a test case.

For JUST we collected a scenario file — ~50 real and hypothetical cases. Every rule in the system has at least 2-3 scenarios that cover it. When someone changes the logic, CI runs all scenarios and shows which results changed. If they changed unexpectedly → bug.

The legacy system had nothing like it. After 15 years nobody knew exactly how something was computed — it had to be reverse-engineered. One of the first deliverables of our implementation was getting scenario expectations from a business analyst and thereby pinning down the actual rules.

4. Dual-run before the cutover

Before legacy → new cutover we ran both systems in parallel for 3 months on the same data. Results were compared every month. A delta > 0.01% triggered an alert.

We hit 7 rules where the new system computed differently. Five times the legacy was right and we had a bug. Twice the legacy was wrong and the business had never noticed (~15 000 CZK underpaid across 3 months). Reported, sellers got corrections, business appreciated it.

Dual-run isn't a luxury. For critical financial systems it's the minimum to launch without surprises.

5. Recalculation capability from day one

Being able to recompute any historical month is load-bearing. Reasons:

  • Audit finds a bug in 2022 rules — you must recompute the entire history
  • Legal dispute with a seller — you must present the detail of the calculation
  • Business wants to retroactively apply a new bonus — recalculation with a retroactive rule

If you have immutable rules in the DB + input snapshots per calculation, recalculation is deterministic. If you have logic in code and you change it routinely, three years on recalculation is impossible.

Generalising

MLM isn't the only use case. The same pattern applies to:

  • Loyalty programs with changing rules
  • Payroll / comp systems
  • Pricing engines where customers have historically negotiated terms
  • Fintech fee calculations with regulatory changes

Common denominator: today's rule ≠ next year's rule, and you have to defend both. Don't put it in code. Put it in data, with immutable history and tests as a contract between business and engineering.

Working on something similar?

Book a 30-minute technical call. No sales process — direct architectural feedback.

Pick a time

Architecture, cloud and integration for complex systems. A senior architect on every project.

Navigation

ServicesHow we workInsightsCase StudiesCareerContactAgency vs. freelancer vs. us

Services

DevelopmentCloudDevOpsAI & DataConsultingDelivery

Contact

CodeDock s.r.o.

Zlenická 863/9, 104 00 Praha 22

Czech Republic

info@codedock.com

Company ID: 14292769

VAT ID: CZ14292769


© 2026 Codedock

ContactPrivacy Policy
Book a call