The Finite State Machine of Spending: Debugging Your Financial Source Code
Using the Finite State Machine model from computer science to debug impulse spending and write cleaner financial logic.
I’ve always found traditional budgeting advice to be slightly broken. It usually sounds something like this: “You spent too much on dining out last month. Stop doing that.”
That’s like telling a piece of software that crashed, “Hey, stop crashing.” It’s not helpful. It addresses the output, not the logic that produced it.
In my quest to treat my life like a system that can be engineered, I’ve started looking at my finances through a different lens. I don’t just look at the spreadsheet, the logs, I look at the algorithm.
Specifically, I’ve been mapping my spending habits using a concept from computer science called a finite state machine, or FSM. If you’re not a developer, an FSM is a way of modeling a system that exists in one of several specific “states” at any given time, and transitions from one state to another based on “inputs,” or triggers.
Your toaster is a state machine. State: off. Input: you push the lever down. Transition: it moves to state toasting. Input: time runs out. Transition: it pops up and returns to state off.
Simple enough. You are also a state machine, and your wallet is the output device. Map out the states you go through before you buy something, and you can find the bugs in your code, the infinite loops draining your bank account.
Mapping the spaghetti code of impulse buying
Most of us think spending is a binary action: we see something, we buy it. But when you slow it down, there is always a sequence of states.
For a long time, my “Impulse Buy” state machine looked like this:
- State: IDLE. (I am bored/tired/anxious).
- Input: I open a social media app.
- Transition → State: BROWSING. (Passive consumption).
- Input: I see a targeted ad for a “productivity gadget.”
- Transition → State: FANTASY. (I imagine how this gadget will fix my entire life).
- Input: “One-click purchase” button.
- Transition → State: TRANSACTION. (Money leaves).
- Transition → State: REGRET. (3 days later).
This is what developers call spaghetti code. It’s messy, reactive, and prone to errors. The transition from browsing to transaction happens too fast. There’s no error check, no guard clause.
When I looked at my credit card bill, I wasn’t seeing items I’d bought. I was seeing instances where my internal code had executed a bad loop.
Identifying the inputs
In an FSM, the system sits in a state until an input triggers a change. Debugging your finances means identifying the inputs: internal ones like boredom, hunger, insecurity, the 3pm dopamine dip; external ones like push notifications, email newsletters, sale banners, a friend showing off their new car.
For me, one input accounted for most of the damage: email newsletters. I’d be idle, an email would land saying “Flash Sale,” and my system would transition straight to browsing. I didn’t need the item, wasn’t looking for the item. The input alone forced the state change.
The fix was blunt: I unsubscribed from every retail newsletter. I removed the input. Now the system stays idle, or, on a good day, creating, because that trigger no longer exists.
Writing a guard clause
In programming, a guard clause is a check that runs at the start of a function; if the conditions aren’t met, the function stops before it does anything. I needed one between the fantasy state and the transaction state.
Mine is a 72-hour waiting buffer. The state machine now looks like this: state desire (I want the thing), input: attempt to buy, guard clause checks whether it’s a recurring utility or under $20, and if not, holds. I write the item down in a wishlist note instead of buying it.
Holding is a valid state. I’m allowed to want the thing, I just can’t transition to a transaction yet. The holding state has a decay function: after 72 hours the initial dopamine rush usually fades, and most of the time the next transition isn’t to transaction, it’s to delete. The system resets to idle without spending a dime.
The garbage collection state
In languages like Java or Python, there is a process called “Garbage Collection.” The system automatically looks for memory that is being used by objects that are no longer needed and clears it out to free up resources.
We hold onto physical items and subscriptions long after they’ve stopped providing value, staying in the ownership state out of inertia. Once a month I run a manual garbage collection pass on my finances: look at every subscription, ask whether it still points to a valid goal in my life, and if the answer is no, terminate it. It sounds cold, but it’s liberating. It frees up cash and mental energy for whatever is actually running the current version of my life.
Debugging the sunk cost loop
The most dangerous bug in the human mind is the sunk cost fallacy. In state machine terms, it’s an infinite loop: state owning a boat (or an expensive hobby), input it costs money to maintain, logic “but I already spent so much on it, I can’t sell it now,” transition back to owning a boat. The loop drains resources indefinitely.
A well-architected system recognizes when a process has hung and kills it. If a hobby, subscription, or car is draining more than the value it returns, the code is stuck, and you need to force a hard reset: if output stays below input for three months, sell it.
You are the architect
The reason this metaphor matters is that financial freedom is, underneath it, a system reliability issue. A fragile system, one that’s prone to crashing whenever a sale happens, never builds enough capacity for the big things. It just keeps patching bugs.
Map your purchasing loops and you stop shaming yourself for being bad with money and start treating it as what it actually is: identifying the triggers, building better buffers, cleaning up the waste. Your life is the project. Money is just the resource allocation. Write cleaner code.