Measured, not asserted
Published
The guard in this sample did its job perfectly. It refused every fee it was written to refuse, on every row, and reported an accurate reason for each one. The borrower was still reported past due, because the guard was written about money.
One name, two outcomes
A late payment produces two things and they are decided separately. There is a fee, which is money, and there is a delinquency status, which is what a credit bureau is told. A single grace period governs both.
Servicers guard the fee. It is the thing that gets litigated, so the sample carries a rule of record that mirrors real practice: no fee inside the note's sixteen-day window, and no fee above the state's statutory maximum. Nothing guards the status, because a status is not a charge and nobody writes a rule about it.
Configured correctly, a borrower who paid on the eleventh -- ten days past due, inside the note's fifteen-day grace -- gets no-fee(within 15-day grace) status=CURRENT. Protection alone, on five presets, left every row of this file identical.
The guard held and the credit report went anyway
Renaming gracePeriodDays reverts it to the library's default of zero, so every payment made after the due date is late. The fee rule caught this immediately and completely. Every row came back fee-rejected(note-window-check(no fee before day 16)). Not one improper dollar was charged.
And every one of those rows also came back status=PAST-DUE. The borrower who paid on the eleventh, inside their grace period. The borrower who paid on the third, two days after the due date, with the full amount. Both reported past due, by a system whose own guard had just refused to charge them anything.
That is the finding this file was built around. The lost name reached two outcomes; the servicer had written a rule about one of them; and the rule worked exactly as specified while the other outcome travelled unguarded straight to a credit file. A guard covers the outcome it was written for, and the fact that it is passing tells you nothing about the outcome beside it.
The louder version, and the quieter one
Renaming gracePeriodDays and requireDpdCheck together removes the fee rule as well, and produces the version that at least generates complaints: fee($47.10) status=PAST-DUE against a payment made two days after the due date. That is the enabling-flag shape this series has now measured repeatedly, and it is the one an angry phone call eventually surfaces.
The fee rate arm behaves the same way. Renaming lateFeePct reverts 2% to the library's 5%, giving $117.76 on a $2,355.11 instalment, and the statutory cap rule refused it outright. Renaming the flag as well assessed it -- a fee above the state maximum, on a genuinely late payment, reported as a success.
The quietest arm is the date. Renaming paidDate means no payment can be dated, and the engine reads an undated payment the only way a servicer safely can: as not received. Every instalment in the file, including the one paid on the third, came back dpd=34 settled=false, carrying a fee and a status of REPORTABLE-30. Every account in the book, thirty days delinquent, on a credit file.
What failed safely
Renaming dueDate produced undated-instalment(refused before assessment) and nothing was assessed at all. That is the correct answer -- with no reference date there is no question to answer -- and it is a useful contrast with the paid date one field away, which has a defensible reading available and therefore fails open.
The direction here is decided by whether the engine can substitute a defensible assumption. It cannot invent a due date, so it refuses. It can treat an undated payment as unreceived, which is the safe reading for the lender and the harmful one for the borrower, so it proceeds. Neither is a bug in the engine; both follow from what the missing name meant.
The controls behaved as predicted, as they have for twenty consecutive rounds of these measurements. The two option values pinned equal to the library's own defaults -- the minimum fee and the currency -- produced no change at all when renamed, which is what an option whose value already matches the default must do.
What to do about it
Member renaming is opt-in and the pattern is yours to write, so the first mitigation is the same as ever: keep option keys read by an installed library out of it, and keep out the field names on records that cross into one.
The second one is specific to this file and costs nothing at design time. When one input decides a charge and a report, guard both, or derive one from the other. A status that is computed independently from the same grace period is a second chance to get it wrong; a status derived from the fee decision you already validated is not.
And treat an undated payment as an exception rather than as an unpaid one. The safe reading for the lender and the correct reading are not the same thing, and the gap between them ends up on somebody's credit file.
Frequently asked questions
Does obfuscation change how delinquency is assessed?
Not on its own. Across five presets and five areas measured this pass, protection alone left every output identical -- twenty-five comparisons, no differences. What changed behaviour was member renaming, a separate opt-in option whose pattern you write yourself.
What actually went wrong in the measured case?
Renaming the grace-period option reverted it to the library's default of zero days. Every payment made after the due date became late, including one made two days after with the full amount.
Did the servicer's own rule catch it?
For the fee, completely. Every improper fee was refused with an accurate reason and not one wrong dollar was charged. The delinquency status was reported past due on exactly the same rows, because the rule was written about money and a status is not a charge.
Why does that matter more than the fee?
A fee is refundable and a borrower notices it. A past-due status goes to a credit bureau, affects the borrower's ability to borrow elsewhere, and is far harder to unwind -- and in this measurement nothing at all stood in its way.
What was the widest-reaching arm?
Renaming the field carrying the payment date. An undated payment is read as not received, which is the only safe reading for a lender, so every instalment in the file came back 34 days past due with a fee and a reportable status -- including one paid on time and in full.
Did anything fail safely?
Yes. Renaming the due date was refused outright, because with no reference date there is no question to answer. The direction turns on whether the engine has a defensible assumption available, not on which field sounds more important.
What is the practical mitigation?
When one input decides both a charge and a report, guard both or derive one from the other, so the validated decision is the one that reaches the bureau. Treat an undated payment as an exception rather than an unpaid one, and keep library-read names out of your rename pattern.
Related reading