Elhadi.
← All posts
5 min readAI-written · in Suhaib Elhadi's voice

Forgiving a debt should be a button

This update was drafted on a schedule by the AI I build with, from real project notes — part of the vibecoding experiment this blog documents.

I've written before that forgiving a tab is a real action, not an absence — that letting a debt quietly expire just rebuilds the group chat with extra steps. I still believe that. But I said it in a paragraph and moved on, and the paragraph skips the part that was actually hard to build. Which is: once you decide forgiveness is a button, you have to design the button. And a button that clears a public debt someone else owes you is about four decisions deep before it's safe to ship.

So this is the design post. What forgive has to do, what it must not do, and why almost every version of it I sketched first was slightly cruel.

Forgive is not delete, and the difference is the whole feature

The first instinct is to make forgive and delete the same thing. Debt's gone either way, right? The other person owes nothing, the number's off the books. Why have two.

Because they say opposite things about what happened.

Delete says this never should have been recorded. It's for the mistake — wrong amount, wrong person, you paid it back in cash and I forgot to close it. Delete is an admission that the ledger was wrong, and the honest thing is for it to vanish like it was never there.

Forgive says this was real and I'm choosing to end it. The money was owed. It's not owed anymore, not because it was paid and not because it was a mistake, but because I decided. That's a gift, and a gift you can't see isn't quite a gift — it's just something that didn't happen.

In tab. those are two different actions with two different results in the record. A forgiven tab closes and stays visible as forgiven. A deleted one is gone. If you collapse them, you lose the ability to be generous on purpose, and being generous on purpose was the point.

Only the lender can press it

This one sounds obvious until you think about who wants the button.

The person who owes money would love a forgive button. Of course they would. But if the debtor can forgive their own debt, it's not forgiveness, it's just a walkout with a nicer label. So forgive is only ever available to the person owed. You can clear what someone owes you. You can never clear what you owe someone — you can only pay it, or ask them to forgive it, which is a conversation, not a tap.

That asymmetry is deliberate and it's the same shape as the rest of the app. The lever always sits with the person who covered the money, because they're the one who ate the risk. What the software does is make using that lever — in either direction, ask or forgive — cost less than the equivalent text message.

The flex problem

Here's the one that actually held up the design.

The ledger is public. So a forgiven tab is public. And "Elhadi forgave a $60 tab from [name]" read the wrong way is not generosity — it's a billboard that says I carried this person and I want everyone to know I let it go. That's worse than never forgiving. You've turned a private kindness into a status move at someone else's expense.

I went back and forth on just making forgiveness silent. No notification, no visible state, the number quietly goes to zero. But that's the expiring-debt problem again — if the other person never clearly learns they were forgiven, you didn't give them anything, you just stopped tracking. The generosity has to land or it isn't real.

Where I ended up: the person forgiven gets told, plainly and privately — the notification is to them, it's not an announcement. In the public record the tab reads as closed, and "forgiven" is available if you look, but it's not broadcast, not ranked, not celebrated in the UI. No confetti. Forgiving doesn't earn you a badge. The tone target was a receipt stamped paid, not a press release. The two people involved know exactly what happened. Everyone else sees a tab that's no longer open, which is all they need.

The rule I kept coming back to: forgiveness should be legible to the person it helps and boring to everyone else.

What forgive looks like in practice

Three things, and I tried hard to keep it to three:

  1. It's on the tab, next to settle. Same weight as paying — not hidden in a menu, not styled like a destructive action with a red warning. Forgiving isn't dangerous. It's just final.
  2. It asks once. A confirm step, because it can't be undone and because a mis-tap here isn't like a mis-tap elsewhere — you'd be telling someone they're clear when they're not. One "are you sure," then it's done.
  3. It closes the record the same way settling does. The tab moves to the same place a paid tab goes. In the ledger, forgiven and settled sit side by side as closed — the friendship's account is square, and how it got square is the smaller detail.

No amount field — you forgive the whole thing or you don't. No "forgive but leave a note." I sketched a partial-forgive once (knock it down to half) and killed it, because now you're negotiating, and the entire value of the button is that it ends the negotiation.

Why bother building it at all

You could argue the whole feature is unnecessary. If you want to forgive a debt, just... don't collect it. Never mention it. It'll fade.

That's the argument I hear most and I think it's wrong for the same reason the app exists. The debt doesn't fade — it goes quiet, and quiet is where these things rot. An unspoken "I guess we're not talking about that $60" sits between two people and means something neither of them will say. The forgive button's real job is to convert that silence into a small, clear, closed event: I cleared it, you know I cleared it, we're done.

The hard part of lending money to a friend was never the arithmetic. It's the social cost of every possible next move — asking, not asking, bringing it up, letting it slide. Forgiveness is one of those moves, and left undesigned it's the passive one, the one you do by omission and hope was understood. Making it a button is just refusing to let the kind option be the invisible one.

This one's auto-drafted from my notes on a schedule. If a number isn't in the notes, it doesn't show up here — I'd rather leave a blank than make something up.