On this page · 6 sections
Start from the field, not the program
The question a lending product owner asks is simple: how does the system calculate the interest on this loan? Most discovery teams answer it by finding the program with ACCRUE in its name and reading it. That finds the formula. It misses the eligibility rules, the day-count basis selection, the rounding step in a separate posting program, and the month-end adjustment that runs in a different JCL stream.
A better starting point is the field that holds the answer. In the core we will use as an example, a commercial lending system of about 1.8 million lines, that field is LN-ACCRUED-INT in the loan master record. A backward program slice from every write to that field, across programs, gives you every statement that can influence its value. Everything outside the slice is irrelevant to the question, however interesting it looks.
The core paragraph
The slice starts here. The daily accrual is computed with a day-count basis chosen by an 88-level condition, zeroed for loans in non-accrual status, and added to the running accrued balance.
Three things in these fifteen lines are business rules: the choice of day-count basis, the non-accrual override, and the precision of WS-DAILY-INT. That field is declared PIC S9(7)V9(6) COMP-3, carrying six decimal places so that daily accruals do not lose sub-cent amounts. Rounding to cents happens later, in the posting program, at month end. Collapse those two steps into one and the interest on a large balance drifts by a few cents a month, which is enough to fail reconciliation.
1 3200-ACCRUE-DAILY.2 IF LN-DAYCOUNT-ACT3653 COMPUTE WS-DAILY-INT ROUNDED =4 LN-PRIN-BAL * LN-RATE / 3655 ELSE6 COMPUTE WS-DAILY-INT ROUNDED =7 LN-PRIN-BAL * LN-RATE / 3608 END-IF9 IF LN-STATUS-NONACCRUAL10 MOVE ZERO TO WS-DAILY-INT11 END-IF12 ADD WS-DAILY-INT TO LN-ACCRUED-INT13 ON SIZE ERROR PERFORM 9200-ACCRUAL-OVERFLOW14 END-ADD.15 3200-EXIT.16 EXIT.
Following the slice outward
The interesting rules are not in that paragraph. They are in the places that set its inputs.
LN-DAYCOUNT-ACT365is an 88-level onLN-DAYCOUNT-CD. The slice shows that code is set at loan boarding from the product table, and overwritten in one place: a maintenance transaction that converts certain participations to 30/360. That second write is a rule nobody listed.LN-STATUS-NONACCRUALis set by a delinquency program when days past due exceed a threshold read from a parameter file, unless a manual hold flag is set. The threshold is data; the hold exception is a rule.LN-RATEfor variable loans is recomputed nightly from an index and a margin, with a floor and a cap. The floor is applied before the cap. If both are breached because of a data error, the cap wins.- The month-end posting program rounds
LN-ACCRUED-INTto cents half away from zero and carries the remainder forward in a separate field.
Each of those becomes a decision table row or a formula with its own source reference. The accrual calculation is not one rule. It is a small decision model with seven decisions, and only one of them is in the program everyone pointed to.
The 30/360 detail that is not 30/360
Read the code closely and you notice the ELSE branch divides by 360, but the daily paragraph runs once per calendar day. That is actual/360, not 30/360. True 30/360 would require adjusting day counts at month boundaries, and this program does not. The product documentation calls the basis 30/360. The code implements actual/360. For a year with 365 days that is about 1.4% more interest than the documented basis would produce.
Whether that is a defect or the policy is not a question static analysis can answer. It is exactly the question the sign-off view is for. The extracted rule shows the behavior, the code, and a note that the documented name and the computed behavior differ. The lending product owner decides which one the new system implements, and if they choose the documented basis, the change is a conscious product decision with a customer communication plan, not a migration accident.
Tests that prove the rewrite
The equivalence suite for this decision model is generated from the branch predicates in the slice. Each fixture names the rule it exercises.
1[2 { "rule": "ACCR-BASIS-ACT365", "given": { "principal": "250000.00", "rate": "0.0725", "dayCount": "A" },3 "expect": { "dailyInterest": "49.657534" } },4 { "rule": "ACCR-BASIS-ACT360", "given": { "principal": "250000.00", "rate": "0.0725", "dayCount": "3" },5 "expect": { "dailyInterest": "50.347222" } },6 { "rule": "ACCR-NONACCRUAL", "given": { "principal": "250000.00", "rate": "0.0725", "status": "N" },7 "expect": { "dailyInterest": "0.000000" } }8]
Why the slice is the deliverable
Check the arithmetic: 250,000 times 0.0725 is 18,125; divided by 365 that is 49.6575342..., rounded to six places 49.657534; divided by 360 it is 50.3472222..., rounded to 50.347222. A new Java or Go accrual service has to reproduce those to the sixth decimal, day after day, and then post the same cents at month end.
A slice-based inventory turns an open question, what does this system do with interest, into a closed list of decisions with code behind each. That is what lets a banking modernization program commit to a build estimate. The migration guide for COBOL to Java shows how the same slice maps to Spring services.