A risk based testing approach for fintech: the scoring matrix we use
Our risk based testing approach for fintech scores each test item by impact per failure (money moved, regulatory exposure, customer harm) and by likelihood (change frequency, integration count), and multiplies the two scores into a risk level that sets test depth. In our practice, a top-level money or regulatory item never drops below the second of four depth tiers.
Mira Kowal, Senior QA engineer, lending, BNPL and wealthtechPublished Updated
What the ISTQB definition fixes, and what it leaves to you
The ISTQB Glossary (term version 3, undated, read 2026-09-22) defines risk-based testing as "A test approach in which the management, selection, prioritization, and use of test activities and resources are based on corresponding risk types and risk levels." The risk level entry reads "The measure of a risk defined by risk impact and risk likelihood."
The ISTQB Foundation Level syllabus v4.0.1, dated 15 September 2024, gives the method in section 5.2.3: "In the quantitative approach the risk level is calculated as the multiplication of risk likelihood and risk impact. In the qualitative approach the risk level can be determined using a risk matrix." The same section says the analysis results are used to "Determine the test techniques to be employed and the coverage to be achieved" and to "Estimate the test effort required for each task".
Section 5.2.2 lists "incorrect calculations" among product risks, and "Loss of revenue, trust, reputation" and "Criminal penalties" among their consequences. The syllabus does not say how to score them; on a lending product, that choice decides whether a late fee rule gets a boundary suite or a smoke check.
Why fintech impact needs three columns
A single severity field ranks a crashed settings screen and a double interest posting on similar terms. We split impact into three columns, each with its own owner and recovery path:
- Money moved per failure. An accrual job that runs twice after a retry posts a second day of interest to every active loan, and repeats each night until reconciliation catches it.
- Regulatory exposure. The annual percentage rate (APR) on an offer screen moves no money. Regulation Z still states that the APR "shall be determined in accordance with either the actuarial method or the United States Rule method" (12 CFR 1026.22(a)(1), eCFR up to date as of 2026-09-18).
- Customer harm. A repayment debited twice can overdraw the borrower's bank account at another bank, a loss that never appears in the lender's ledger.
We take the highest of the three as impact. An average lets a harmless display column dilute a level 4 money column.
Likelihood gets two inputs from the repository and architecture diagram: how often the code changed, and how many external systems the item touches. Each integration adds failure paths that the main path never runs: timeouts, duplicate events, late webhooks, and returns.
The scoring matrix we use
This is our practice. ISTQB defines risk level from impact and likelihood; the scales, the highest-of-three rule, the tiers, and the floor below are ours.
| Score | Money moved per failure | Regulatory exposure | Customer harm | Change frequency | Integrations touched |
|---|---|---|---|---|---|
| 1 | No money moves | No named rule | Cosmetic defect | No change in the last 12 months | None |
| 2 | One account, reversible the same day | Partner bank or lender contract term | Defect with a workaround | Changed in the last 12 months | One |
| 3 | Many accounts, or a ledger posting | Named rule on timing or records, or a network rule | Wrong amount shown or charged | Changed in at least 6 of the last 12 months | Two or three |
| 4 | Funds leave the platform, or a batch or settlement amount is wrong | Named rule on what the customer is charged or told | Lost access to funds, or a missed payment | Changed in this release | Four or more |
The formula:
- Each column takes the highest band that matches.
- Impact is the highest of money, regulatory, and customer.
- Likelihood is change plus integrations, divided by 2, rounded up.
- Risk is impact times likelihood, from 1 to 16.
- Tier D1 covers risk 12 to 16, D2 covers 8 to 9, D3 covers 4 to 6, and D4 covers 1 to 3.
- Floor: an item with money or regulatory at 4 gets at least D2.
| Tier | What we test | When it runs | Evidence kept |
|---|---|---|---|
| D1 | Written specification with worked reference values, boundary tests on every input, an independent reference model, and failure paths per integration | Every merge, plus a reconciliation replay before release | Run report per release with build and environment |
| D2 | Boundary tests on documented limits and one failure path per integration | Every merge | Run report per release |
| D3 | Main path plus one boundary per input | Nightly | CI history |
| D4 | Smoke check | Release suite | CI history |
The worked rows describe a synthetic consumer lending product. Keep the CSV in the repository next to the tests:
item,money,regulatory,customer,change,integrations
daily interest accrual on instalment loans,3,3,3,3,2
late fee after the grace period,2,4,3,2,3
repayment collection by ACH debit and returns,4,3,4,3,4
APR on the loan offer screen,1,4,3,2,2
early payoff quote,3,3,3,1,2
lead routing to partner lenders,2,2,2,3,4
statement PDF layout,1,1,1,2,1
disbursement to the borrower bank account,4,2,4,1,3
adverse action notice,1,4,2,1,1The script reads that file as risk_matrix.csv and prints the tier per item:
import csv, math
TIERS = [(12, "D1"), (8, "D2"), (4, "D3"), (1, "D4")]
def score(row):
m, r, c = (int(row[k]) for k in ("money", "regulatory", "customer"))
f, n = int(row["change"]), int(row["integrations"])
impact = max(m, r, c)
likelihood = math.ceil((f + n) / 2)
risk = impact * likelihood
tier = next(t for floor, t in TIERS if risk >= floor)
floor = (m == 4 or r == 4) and tier in ("D3", "D4")
return impact, likelihood, risk, "D2" if floor else tier, "*" if floor else " "
with open("risk_matrix.csv", newline="") as fh:
rows = [(row["item"], *score(row)) for row in csv.DictReader(fh)]
for item, i, l, s, t, mark in sorted(rows, key=lambda r: (r[4], -r[3])):
print(f"{t}{mark} {s:>2} = {i} x {l} {item}")Output with Python 3.13.1:
D1 16 = 4 x 4 repayment collection by ACH debit and returns
D1 12 = 4 x 3 late fee after the grace period
D2 9 = 3 x 3 daily interest accrual on instalment loans
D2 8 = 4 x 2 APR on the loan offer screen
D2 8 = 2 x 4 lead routing to partner lenders
D2 8 = 4 x 2 disbursement to the borrower bank account
D2* 4 = 4 x 1 adverse action notice
D3 6 = 3 x 2 early payoff quote
D4 2 = 1 x 2 statement PDF layoutTwo rows show why the rules exist. The adverse action notice scores only 4, because its template rarely changes and touches no external system. Regulation B states that an adverse action notification "shall be in writing" (12 CFR 1002.9(a)(2), eCFR up to date as of 2026-09-18). Under 1002.9(d), a creditor that "did not receive more than 150 applications during the preceding calendar year" satisfies the section with oral notifications. Either way the floor lifts it to D2. The early payoff quote sits at D3 while its code is stable; set change to 4 in the release that touches it, and risk becomes 9 and the item moves to D2.
How we run the matrix in a release
- Score items, not features. One row is one calculation, one state transition, or one file format. A single "repayments" row hides the higher-scoring return path.
- Re-score on three triggers. A pull request that touches the item raises change. A new partner bank or processor raises integrations. A launch in a new state or country raises regulatory exposure.
- Review the CSV diff in the pull request. A tier drop then sits next to its cause.
- Write failure paths for D1 before any defect appears. The ACH row gets tests for a return after the funds were marked available, a duplicate debit file, and a late return code.
For D1 calculation rows, the independent reference model and boundary suite come from financial calculation testing. The accrual row's leap year and month-end cases are in our interest accrual and day count guide. Where no scored inventory exists yet, a QA audit and test strategy engagement builds one and turns it into a risk-based test strategy and roadmap. The failure patterns behind the lending rows are on our consumer lending testing page.
Sources
- ISTQB Glossary, risk-based testing, term version 3 (undated, read 2026-09-22),
- ISTQB Glossary, risk level, term version 2 (undated, read 2026-09-22),
- ISTQB Certified Tester Foundation Level Syllabus v4.0.1, section 5.2 Risk Management,
- eCFR, 12 CFR 1026.22 Determination of annual percentage rate (up to date as of 2026-09-18),
- eCFR, 12 CFR 1002.9 Notifications (up to date as of 2026-09-18),
Related
Guides
Scope your testing
Send FintechAssuranceLabs your product, segment, money flows, and the standards that need evidence, and get a proposed test scope with its deliverables.
Scope your testing