case studies

A Credit Limit Dropped From 100 to 50 and XKV Shows the Three Steps Behind It

A customer calls support. Their credit limit was 100 yesterday and it’s 50 today, and nobody told them why. The support agent can see the 50. Everything else is spread across three services.

The limits service set the 50, under its risk policy. The risk model raised the account’s score the day before. Billing had recorded a chargeback from the bank that morning. Each service did one reasonable thing, and each logged it in its own way. Putting the story together means knowing which logs to open and in what order.

With XKV, each service writes its value with the change it acted on as the cause. The support agent asks one question and gets the whole chain:

The why tree for account 42’s limit: the limit of 50 set by limits-service under risk policy v3, caused by the risk score of 0.82 from risk-model after a new chargeback, caused by the chargeback billing recorded from a bank notice with ref BANK-7731.

What Each Service Writes

Billing records the chargeback, with the bank’s reference:

billing := xkv.WithActor(ctx, "billing-worker")
cb, err := db.Put(billing, "chargeback:cb_991", []byte("card dispute filed"),
	xkv.Reason("bank notice"), xkv.Ref("BANK-7731"))

The risk model reacts to it and says so, pointing at that exact revision:

risk := xkv.WithActor(ctx, "risk-model")
_, err = db.Put(risk, "risk:42:score", []byte("0.82"),
	xkv.Reason("new chargeback"), xkv.Cause(cb.Key, cb.Rev))

The limits service applies its policy to whatever the score is now:

limits := xkv.WithActor(ctx, "limits-service")
_, err = db.Put(limits, "account:42:limit", []byte("50"),
	xkv.Reason("risk policy v3"), xkv.CauseLatest("risk:42:score"))

None of the three needs to know about the others. Each one names the input it read, which it has in hand at the moment it writes.

What Support Sees

account:42:limit = 50   r5
│ was 100 · by limits-service · risk policy v3
└─ risk:42:score = 0.82   r4
   │ was 0.31 · by risk-model · new chargeback
   └─ chargeback:cb_991 = card dispute filed   r3
        by billing-worker · bank notice · ref BANK-7731

Read from the top, it’s the answer to the customer: the limit went down because the risk score went up, and the score went up because of a disputed card payment. The bank reference is there for whoever handles the dispute. Each step also shows the value before it, so “was 100” and “was 0.31” come with the explanation.

When the Dispute Is Settled

Say the customer settles the dispute and an agent raises the limit back. That write points at the chargeback too, so the next person to ask why gets that story as well. In the live demo, the credit limit story loads this exact chain, and you can make that write yourself and watch it join.

Run It Yourself

The case study is a Go program in the code repository:

git clone https://github.com/explainable-kv/xkv
cd xkv
go run ./examples/account-limit

The quick start builds the same chain with the xkv command.