Skip to main content

When I went looking for a debt payoff calculator, I found plenty of options—but they all wanted my financial data. Bank account connections, cloud sync, accounts that track everything. For something as personal as debt, that felt wrong.

So I built PayoffPilot, a privacy-first debt payoff calculator that keeps all your data on your device.

That’s the tidy version. The real version took two and a half years, and most of that was not spent writing code.

The Honest Timeline

I had the idea in January 2024. I wrote nothing.

What happened in between is the part nobody puts in a case study: I was learning my actual job. I’d moved into IBM Vault administration at Dell (HashiCorp Vault at the time) — secrets management at enterprise scale, policies and auth methods and lease lifecycles across a large organization. That is a lot to absorb. Add two kids and the hours that are genuinely mine after everyone’s asleep, and a side project stops being a matter of motivation. There just wasn’t room.

The first commit in the PayoffPilot repository is dated October 8, 2025. Nine months later, the app went to Apple for review.

What changed wasn’t discipline. It was that AI-assisted development made the work fit the time I actually had. An hour after the kids are down isn’t enough to hold an entire codebase in your head, remember where you left off, and make progress. It is enough when something else is holding the context for you. That’s one factor among several—the job got less unfamiliar, the scope stayed small—but it’s the honest answer to why October 2025 was different from January 2024.

The Problem

The typical debt payoff app workflow:

  1. Create an account
  2. Connect your bank accounts via Plaid or similar
  3. Hope they don’t get hacked
  4. Watch ads or pay a subscription

I wanted something different: no account, no cloud sync, no data collection, and one payment if any at all.

Privacy as Architecture, Not Policy

The first decision was structural: no backend. Not “we promise not to look at your data”—no mechanism by which the data could leave.

Everything persists locally through SwiftData:

import SwiftData

@Model
final class Debt {
    var id: UUID
    var name: String
    var classification: DebtClassification
    var currentBalance: Double
    var annualPercentageRate: Double
    var minimumMonthlyPayment: Double
    var paymentDueDate: Date?
    var createdDate: Date
    var lastUpdated: Date
}

No API calls, no analytics SDK, no third-party tracking. A privacy policy is a promise. An app with no networking layer is a guarantee, and the difference matters to me as someone who spends his day job thinking about what happens when secrets end up somewhere they shouldn’t.

Separating Calculation From Storage

The design decision I’m happiest with is one nobody will ever see. The payoff math doesn’t operate on SwiftData models—it operates on plain value-type snapshots:

nonisolated protocol PayoffCalculator {
    func calculatePayoffSchedule(debts: [DebtSnapshot], monthlyBudget: Double) -> PaymentSchedule
}

extension PayoffCalculator {
    func calculatePayoffSchedule(debts: [Debt], monthlyBudget: Double) -> PaymentSchedule {
        let snapshots = debts.map { DebtSnapshot(from: $0) }
        return calculatePayoffSchedule(debts: snapshots, monthlyBudget: monthlyBudget)
    }
}

DebtSnapshot is a struct. The calculation engine never touches the persistence layer, which means the interesting logic can be tested without standing up a model container, and a scenario simulator can project “what if I added $200 a month” against copies without ever risking the user’s real records.

The two strategies then become two implementations of one protocol:

  • Avalanche — highest interest rate first. Mathematically optimal, saves the most money.
  • Snowball — smallest balance first. Psychologically effective, builds momentum with early wins.

AvalancheCalculator and SnowballCalculator each implement the protocol, and each has its own test suite. Adding a third strategy later means adding a type, not editing a switch statement.

The Bug That Taught Me the Domain

Here’s the one that made me appreciate how much of financial software is edge cases rather than arithmetic.

If a payment is smaller than the interest accruing that month, the naive implementation applies the interest, subtracts the payment, and the balance goes up. Run that across a projection and you get a debt that grows forever and a payoff date of never. This is a real situation—it’s how people end up trapped on a minimum payment for a decade—but a projection that silently runs to infinity is a bug, not a feature.

let computedInterest = balance * debt.monthlyInterestRate
let principal: Double
let interest: Double
if paymentAmount <= computedInterest {
    principal = paymentAmount
    interest = 0
} else {
    interest = computedInterest
    principal = max(0, paymentAmount - interest)
}
let newBalance = max(0, balance - principal)

The handling is deliberate: when the payment can’t cover the interest, the whole payment goes to principal so the projection still terminates and the user still sees a real date. It’s a modeling choice, not a mathematical truth, and it’s the kind of decision you only encounter by building the thing.

What the Day Job Actually Transferred

I expected the Swift to be the hard part. It wasn’t—the hard part was everything around it, and that’s where DevOps helped more than I anticipated.

The App Store screenshots and the video tour are generated by UI tests (ScreenshotTests, VideoTourTests) rather than captured by hand. That’s the same instinct I apply to Vault workflows at work: if you’ll do it more than twice and you’ll do it wrong under pressure, automate it. Regenerating every screenshot after a UI change is a test run, not an afternoon.

The rest of it—clean separation between layers, code you can actually test, thinking about failure modes before shipping—came over from work directly.

Where It Stands

PayoffPilot is in App Review. It is not on the App Store yet, so there are no downloads, no ratings, and no user reviews to report. I’d rather say that plainly than dress up an unreleased app.

What exists today: 62 Swift files, 12 test files covering the calculation engines, the debt model, milestone tracking, and achievement evaluation. Debt entry and editing, a dashboard, a side-by-side strategy comparison, payment logging and history, milestone tracking, payment reminders, PDF export, and an optional Face ID lock. Free with a Pro tier for the heavier features—one purchase, not a subscription.

What I’d Tell Myself in January 2024

Scope is the whole game. As a working engineer with a family, my constraint was never ideas—it was hours. Every feature I said no to is a reason this shipped at all.

Privacy is easier as an architecture than as a promise. Deciding there would be no backend on day one removed a hundred later decisions. Retrofitting that would have been miserable.

A stall isn’t a failure. Twenty-one months passed between the idea and the first commit, and I spent a lot of that time feeling like I’d abandoned something. I hadn’t. I was learning a job that now pays for the machine I build these on.

Finishing is a different skill than building. The last 10%—App Store metadata, screenshots, the review submission, the privacy nutrition label—took longer than a couple of the features did.


PayoffPilot is coming soon from Big Beard Apps. Questions about iOS development or privacy-first design? Find me on LinkedIn.

Raul C. Peña

Raul C. Peña

Senior Software Engineer at Dell Technologies. Air Force veteran, 20+ years as a Texas real estate broker, self-taught coder. Passionate about DevOps, IBM Vault (formerly HashiCorp Vault), and building things that matter.