Xpense Dev Retrospective
Version 1.x of Xpense didn't have a database. Every expense lived as an entry in a single flat file, expenses.json, sitting in the app's private storage folder. Categories and a handful of settings lived in SharedPreferences, Android's built-in key-value store, under a preferences file named xpense_prefs.
| What | Where it lived |
|---|---|
| Every expense | expenses.json, one JSON array, rewritten in full on every change |
| Categories | A JSON-encoded string stored under one SharedPreferences key |
| First-launch flag, onboarding status | Plain boolean SharedPreferences keys |
Think of SharedPreferences as a small labeled box of index cards, one card per setting, quick to check and quick to update. A flat JSON file is more like a single notebook: to add one entry, you actually reopen the entire notebook, add a line, and rewrite the whole thing back to disk.
For a first version, with a small number of expenses and no need for complex queries, this genuinely worked. Reading the whole file into memory and looping over it in Kotlin was fast enough. There was no meaningful performance problem at ten or even a few hundred expenses.
The real turning point wasn't a performance complaint, it was wanting settlements as a real feature. Settlements need to reference expenses, track resolved status, and stay consistent even as expenses get edited or deleted. That's exactly the kind of relational integrity a flat file can't enforce on its own, and a real database can.
Switching storage systems isn't as simple as writing new code and shipping it. Real users already had real data sitting in expenses.json and in SharedPreferences on their phones. The rewrite couldn't just start fresh, it had to carry every existing user's data across into the new Room database, without asking them to do anything, and without losing a single expense. That migration path, and the one real mistake made building it, is the next two posts.
Next up, how Room actually replaced this system, and what the migration path from a JSON file to a real database looked like in practice.