July 2026 · Technology Audit · Bookkeeping · Follow-up

Auditing My Own Code: What I Found When I Turned My Career on My Bookkeeping System

For a decade I have audited other people’s technology. For thirty years I have loved building my own. This is what turned up when I aimed both instincts at the same target ... the bookkeeping system I built with Claude ... and wrote up the findings the way I would for anyone else.

Scott Curtner July 12, 2026 9 min read
An open handwritten ledger beside a laptop showing the same records as a spreadsheet, in warm afternoon light
The same books, kept two ways ... thirty years apart.

I audit technology for a living. For about a decade, my job has been to sit with other people’s systems and ask the uncomfortable questions ... where is the bug, where is the data stale, where is the process quietly wasting everyone’s time. Before that, and all through it, I have spent closer to thirty years as a technologist who loves to build things. AI has taken that second half of me, the builder, and set it on fire again.

Those two halves are most useful pointed at the same thing: my own systems. I am always, quietly, auditing my own work ... and the findings tend to arrive when I stop looking at a problem as one of those people and start looking at it as the other.

The clearest example is a bug Claude had convinced me, for months, could not be fixed. My property spreadsheets use modern Excel formulas that “spill” ... one formula fills a whole table of results and is supposed to grow on its own as new transactions arrive. Mine kept truncating. Every time my Python sync wrote to the workbook, the tables froze at their current size and stopped showing new rows. The fix was manual: open each tab, re-enter the formula by hand. Claude and I had looked more than once, and the answer kept coming back the same ... the library that writes the file cannot preserve what Excel needs, so you will have to do this by hand. I wrote myself an instruction sheet inside the workbook so I would not forget the steps.

I accepted that. As the user of the system, I knew it was a chore, and I did the chore. Then one weekend I asked the question as the builder instead ... not “how do I do this by hand” but “is there any way at all to automate the thing I keep doing by hand?” Same problem, different question. This time Claude went digging deeper and came back with something neither of us had surfaced before. There was a way. That shift, from performing the workaround to refusing to accept it, is the whole story of this piece.

— • —

That was the first of three findings from a few weekends of turning this instinct loose on my own books. I will write all three up the way I would write up anyone else’s ... condition, cause, risk, remediation. Old habit, and a good one.

Finding one: the bug we were told couldn’t be fixed

The spill bug is worth understanding, because the cause was invisible from anywhere inside Excel. Claude and I traced it to the XML inside the workbook file: the library I use to write transactions, openpyxl, does not preserve the small piece of metadata that tells Excel a formula is a dynamic array. Every save quietly rewrote my spill formulas as the older, legacy kind, frozen at whatever size they were at that instant. Nothing looked broken. The formulas still calculated. They just stopped growing.

A laptop showing a bookkeeping spreadsheet with a running-total column and one total cell highlighted
A running total that looks right ... and quietly stops counting a few rows short.

The fix came in two layers. First, Claude moved every tab’s totals off the fragile spill and onto SUMIFS ... a plain formula that reads straight down the ledger and sums everything matching a given property, however many rows there happen to be. The totals are now correct no matter what the spill does, and I kept the spill tables on purpose, so I can audit one against the other. Second came the part that had eluded us for months: a small script, built on a library called xlwings, that drives Excel through its own engine to force a true re-spill across every tab at once, wired to a single button. Installing xlwings was the actual unlock ... it turned out to be the one tool that could do what openpyxl could not. The manual copy, delete, and paste I had done tab by tab, sync after sync, is simply gone.

Finding 01 · Dynamic-Array Spill Degradation

The property tables froze on every sync, and the fix was “do it by hand”

Condition Every entity tab’s spill table silently froze at its current size on each sync and stopped showing new rows. Keeping them current meant re-entering the formula by hand, tab by tab.
Cause openpyxl does not preserve Excel’s dynamic-array metadata, so every script save downgraded the dynamic spills into fixed legacy arrays that could not grow.
Risk Totals that leaned on the spill could understate without any error showing ... plus a standing manual chore, repeated across every tab, every single sync.
Remediation Two layers: rebuilt all totals on spill-independent SUMIFS, kept as a live audit check against the visible tables; and, via the xlwings library, automated the re-spill through Excel’s own engine into a one-click button, retiring the manual step.

Finding two: the income that counted itself twice

The second finding never threw an error. It invented money. I caught it the old-fashioned way, spot-checking an individual property ledger and noticing the income sat higher than it had any right to.

A person at a desk reviewing financial dashboards on a monitor at dusk
The duplicate never announced itself. I caught it the way I catch most things ... by looking.

Plaid, the service that connects the system to my banks, reports a transaction more than once over its life ... first as pending, then a day or two later as posted. Reasonable, except it assigns each version a different transaction ID. My duplicate check keyed on that ID, so the pending copy and the posted copy sailed through as two separate events. For weeks, certain transactions were booked twice ... rent payments and a payroll run, the recurring items most likely to move through that pending-then-posted lifecycle.

The tell, once Claude and I knew to look, was in the descriptions: the pending copy comes through truncated, the posted copy fills in the detail, same amount and account a day or two apart. About $7,070 of income that had never actually arrived twice was sitting in my books as though it had ... two rent payments, a payroll run, and an Amazon order, each counted once for real and once for pretend.

Finding 02 · Pending/Posted Duplication

Recurring income was being double-counted, silently, for weeks

Condition Pending and posted versions of the same transaction were both booked, double-counting recurring income (rent, payroll) and some expenses. No error was ever raised.
Cause Plaid reissues a transaction with a new transaction_id when a pending charge posts. Deduplication keyed on that id, so the two ids read as two distinct events.
Risk Overstated income ... roughly $7,070 of phantom duplicate income, concentrated in the most predictable recurring transactions.
Remediation Skip pending transactions entirely; book only settled ones. Removed the four historical duplicates by hand, preserving the posted copy of each.

The fix was almost embarrassingly small. One line: if a transaction is still pending, skip it. Book only settled ones. That costs a day or two of latency, which for bookkeeping is the right trade ... I would rather my books lag reality by a day than lead it into fiction. Then I removed the four historical duplicates by hand, keeping the posted copy of each.

Finding three: the expense I knew the system would miss

For the third one, I went in already knowing it was there.

A large renovation payment ... $8,818.75 to my contractor ... was never going to show up in my books. It had been paid from a mostly unused account I had excluded from active review, and the importer’s handling of accounts like that one was blunt: skip them, drop the transactions on the floor. So a real, deductible expense against one of my properties sat completely outside the system, invisible by design.

As the user, I knew the number. As the builder, I knew the design skipped it. So the fix was to change the rule: don’t skip anything, give it a home that won’t interfere. Now transactions from accounts I am not actively assigning are quarantined, not skipped. The $8,818.75 surfaced the instant I made that change, and a single rule filed it against the right property.

Finding 03 · Silent Exclusion of Whole Accounts

A real expense sat outside the books, invisible by design

Condition A real, deductible renovation expense of $8,818.75 sat entirely outside the books, paid from an account the importer was configured to skip.
Cause Accounts excluded from active review had their transactions silently dropped rather than captured, so anything paid from them was invisible ... by design, not by accident.
Risk Understated cost basis on the property, and a standing class of transactions the system could discard without a trace.
Remediation Capture-and-quarantine: nothing is dropped. Unassigned transactions route to a visible holding area that stays out of every total. The expense surfaced at once and filed to the right property by rule.
— • —

The same three questions I’d ask a bank

When I sit across from a team whose system just produced a wrong number, I ask three questions, in this order.

The three questions

Can you confirm this is wrong?
If it is wrong ... how would you dimension the risk?
And what is the best path forward?

They sound simple. They are not. The first refuses to cry wolf ... you confirm the problem is real before you alarm anyone. The second is the real audit: how big is this, and how do you know? The third is where a finding earns its keep ... you arrive with a path forward, not just a problem.

I turned those questions on myself this year, and the answers came out mixed, which is the honest way for them to come out. The duplicated income I confirmed the slow way, spot-checking a property ledger until the numbers stopped adding up. The frozen spills and the hidden expense I already knew about ... one a chore I tolerated, one a limitation I assumed I was stuck with. What I feel good about is the second question. Every transaction carries its source and a unique ID. Nothing gets silently dropped anymore ... it gets quarantined, where I can still see it. The totals can be checked against the tables above them. None of that made the mistakes impossible. It made them findable ... a lower bar than perfect, and the only one that has ever actually held up in practice.

— • —

Both halves, pointed at the same thing

Here is the part I did not expect. I have spent a decade finding other people’s bugs, correcting their outdated documentation, and pointing out where their process quietly costs them time. Good work, and I am good at it. But it is someone else’s system ... at the end of the audit I hand over a report, and someone else decides what to do with it.

Doing this to my own system is a completely different feeling. I find the bug and I fix the bug. I question the design and then I get to change it. The auditor’s eye and the builder’s hands are finally attached to the same person, on the same thing, in the same afternoon ... and with AI closing the distance between “I wonder if” and “it works now,” that loop has gotten short enough to be honestly addictive.

Every one of these findings made my system a little more trustworthy. Every fix taught me something I did not know how to do the week before.

I am not maintaining a spreadsheet. I am upskilling myself on my own tools, on my own time, on problems I actually care about ... a strange and wonderful place to be thirty years into loving technology. I feel like a beginner again, with just enough experience to know how rare that feeling is.

Auditor’s Takeaway

The goal is a system whose mistakes cannot hide ... one that captures and carries its own paper trail. One that lets its totals be checked against each other. What surprised me is that building it that way, and being the one who both breaks it and repairs it, turned out to be wonderfully engrossing work pointed at something of my own.

Connect

Let's talk technology audit

Questions about this article, the tools I used, or just want to connect? Find me on LinkedIn.

Connect on LinkedIn

← Back to scottcurtner.com