← Back to all posts

09 Xpense

The Old Storage System

Xpense Dev Retrospective


Before Room, There Was a JSON File

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.

WhatWhere it lived
Every expenseexpenses.json, one JSON array, rewritten in full on every change
CategoriesA JSON-encoded string stored under one SharedPreferences key
First-launch flag, onboarding statusPlain 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.


Why This Was a Reasonable Starting Point

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.


Where It Actually Started Breaking Down

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.


What Made the Migration Itself Necessary

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.


Closing

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.

← Previous 08 - Kotlin and the UI Next → 10 - Migrating to Room