Case file 05 — Club savings & investment manager
99 Not Out
A club that runs on a shared spreadsheet is running on faith: one person, one workbook, one wrong keystroke away from a decade of trust unraveling. 99 Not Out replaces the workbook with a system where the rules are enforced by the machine, not by everyone agreeing to be careful.

The glitch
The club had run for over a decade on a shared workbook: one sheet per year, monthly columns per member, manual formulas for interest and profit shares, one person responsible for never fat-fingering a cell. That works until someone wants to check their own balance without asking. It works until an exiting member's payout needs to net out cleanly against years of history. It stops working the moment two people need to touch the ledger on the same day. The brief was to replace the spreadsheet without losing a single figure it already had right, and to make sure only the treasurer could ever move the money, enforced somewhere stronger than a polite request not to edit that column.
Jacking in
Migrated the full ledger history into a structured data model: annual per-member deposit totals from 2014 to 2025 rolled up from the workbook's monthly sheets, monthly-level detail starting 2026, and exited members' refunds and profit shares recorded as clean allocations instead of quietly deleted or fudged.
Locked financial writes to the treasurer role at the database layer, not the UI layer. Every member can sign in and see the whole club's numbers plus their own history, but Firestore security rules block anyone else from touching a deposit, expense, loan or investment record, no matter what the app's screens seem to allow.
Built the treasurer role as data-driven instead of hardcoded, so it can be granted or revoked from inside the app itself, with a safeguard so a treasurer can never demote themselves out of the role by accident, a door that only locks one way.
Designed member onboarding around zero shared passwords: the treasurer links a member's email in-app, and the member sets their own password through the standard reset flow the first time they sign in. No secrets change hands.
Built the data layer to run on seeded mock data, reconciled against the club's real workbook, during development, then switch automatically to live Firestore the moment production credentials exist, so the whole system was tested before it ever touched a real rupee.
Shipped push notifications straight from the treasurer's own device, no backend server required: every recorded transaction notifies all registered members through Expo's push API, with device tokens scoped so only the treasurer can read them.
Set up an EAS build and OTA update pipeline: a GitHub Action publishes a JS-only update on every push to main, so bug fixes and screen changes reach installed phones in the background, no Play Store release required, full APK rebuilds reserved for native or SDK-level changes only.