Focus Area:

Requirements analysis, Process mapping, Data modelling, Edge-case definition, Real-time sync

Type:

Self-initiated product, designed, specified and built end-to-end

Role:

Sole analyst, designer & builder

Tool:

React, Firebase (real-time sync), PWA, Claude

Duration:

May 2026

Project Summary

A self-initiated project where I took a real, messy problem: two people tracking a shared
16-day overseas trip budget across cards, currencies and a spreadsheet that never synced. To make it worked it end to end as a business analyst would: defining the problem, gathering and prioritising requirements, modelling the data, mapping the flows, and specifying how the system behaves at the edges.

From Spreadsheet Chaos to Real-Time Budget

The Problem

My partner and I were planning a 16-day trip to New Zealand on a fixed budget. We were tracking everything in a shared spreadsheet, and it kept breaking down in practice:

  • No single source of truth: We spent on different cards and cash, so neither knew the real running total.

  • Currency confusion: Spending was in NZD, the budget in SGD, so every entry needed a mental conversion.

  • Committed vs. actual hidden: Already-paid and "pay on arrival" costs looked the same, hiding how much we'd locked in.

  • No live sync: Lagging updates meant double entries and stale numbers.

The underlying need wasn't "a nicer spreadsheet." It was a shared, real-time, single source of truth for budget and plans that both of us could trust during the trip, even with patchy signal.

Stakeholders & their needs

Even at two users, mapping who needs what shaped every requirement.

Stakeholder

Traveller A (logs most expenses)

Traveller B (plans the itinerary)

Both, jointly

Primary needs

Fast entry on the go; confidence the total is current

Same live budget view; day-by-day plan at a glance

One agreed total; fair record of who paid; works offline in NZ

This is where the "who paid" and "real-time sync" requirements came from because of a stakeholder need (settling up fairly, and never working off stale data), not because they were nice features.

Goals & success criteria

I defined success up front, so I could tell whether the build actually solved the problem:

No spreadsheets

Replace the spreadsheet entirely as our day-to-day tool for the trip.

One shared total

Both travellers see the same budget total within seconds of any entry.

Always in SGD

Budget always shown in (SGD), regardless of where money was spent.

Work offline first

Usable offline, syncing automatically when back online.

Requirements (MoSCoW)

I scoped the build by priority so the must-haves shipped before the trip and nice-to-haves didn't derail the timeline.

Non-functional requirements

  • Offline-first: full read/write without connectivity; sync on reconnect.

  • Installable: runs from the phone home screen (PWA).

  • Performance: initial load under ~2s; instant navigation between sections.

  • Accessibility: minimum 44px touch targets, WCAG AA contrast, readable mobile type.

Representative user stories

A sample of how I framed the requirements from the user's perspective:

Non-functional requirements

  • As a traveller, I want to log an expense in either SGD or NZD so that our shared budget stays accurate without me doing conversions in my head.

  • As a traveller, I want to record who paid so that we can settle up fairly after the trip.

  • As a traveller, I want to mark expenses as paid or pending so that I can see our true remaining budget versus what we've already committed.

  • As a traveller, I want my partner's entries to appear on my phone automatically so that neither of us double-logs or works from stale numbers.

  • As a traveller, I want to see only today's plan at a glance so that I know what's next without scrolling the full itinerary.

Process flow & edge cases

The core flow is logging a shared expense. What makes it a business-analysis artefact rather than a UI walkthrough is the exception handling with the questions of "what happens when the money's in another currency" and "what happens when it's committed but not yet paid."

Edge cases I identified

  • Multi-currency: NZD spend converts to SGD before counting, so totals stay comparable.

  • Committed vs. actual: "Pay on arrival" costs track as pending, separate from actual spend.

  • Shared payer: Each expense records who paid and how, for fair settling-up.

  • Unplanned categories: Spend with no budget is surfaced, not absorbed.

  • Offline & concurrency: Both phones write offline and reconcile on reconnect, with sync state always visible.

Data model (overview)

I modelled the data before building, so the structure could support every requirement above:

What I built

A mobile-first, installable web app with five sections, each mapping directly to a requirement:

Overview (dashboard), Itinerary (day-by-day plan), Budget (tracker + expense log), Packing (per-traveller checklists), and Today (the day's plan + quick entry).


Data persists locally and syncs in real time between both phones, with a live sync indicator.

Screen recording of expense being added

Outcome 🚀

Voyado replaced our spreadsheet for the whole 16-day trip which both of us logging independently, one shared total in seconds, working offline.

Reflection

The original logic assumed each person paid only their own costs, which a flawed assumption that surfaced only through real use, then re-specified and fixed. End to end, the project ran the full requirements lifecycle: the build was the artefact, the analysis the point.

What this project demonstrates for a BA role:

I started from a real business problem, identified stakeholders and their needs, gathered and prioritised requirements (functional and non-functional), modelled the data, mapped the process including its exception paths, and validated the solution against the original goals through real use. The build is the evidence but the analysis is the point.

Solution architecture & hosting

Understanding where the app and data live and the trade-offs involved was part of managing the project, not just building it. At a glance:

  • Frontend: A React progressive web app (PWA), installable to the phone home screen, mobile-first.

  • Hosting: Static files served from a host such as Vercel.

  • Datastore: Firebase Realtime Database, a cloud-hosted NoSQL (JSON) store, located in the asia-southeast (Singapore) region — a data-residency point worth noting for a Singapore-based project.

  • Sync & offline: Firebase pushes changes to both phones within ~1–2 seconds via live listeners; the app keeps working offline and reconciles automatically on reconnect.

A requirement driving an architecture decision. The original scope stored everything in the browser's local storage only. The requirement for both travelers to share one live total is what forced the move to a cloud datastore which is a clear example of a business need shaping a technical choice.


Known risk (documented, not ignored). The current security posture — open "test mode" database rules plus a client-side password — is acceptable for a private, two-person app, but would be a blocker at any real scale. The remediation path (proper authentication and database security rules) is understood. Surfacing this as a known risk with a fix, rather than discovering it later, is part of the analysis.

Validation & UAT: bill-splitting fix

I ran user acceptance testing against the app's real use across the whole trip, documenting each scenario as a test case with expected versus actual results. Most passed; one failed and exposed a genuine requirements gap in the settlement logic which I logged as a defect, traced to root cause, fixed, and re-tested to a pass.

The gap
The "who owes who" total came out wrong whenever one person had paid for a mix of shared expenses and the other person's individual expenses. The original logic carried an unstated assumption that each person pays their own individual costs, and only shared costs need splitting. Real spending broke that assumption.


Worked example:

  • Keith pays NZD 100 for a shared dinner → Juan owes Keith 50.

  • Keith pays NZD 60 for Juan's personal souvenir → Juan owes Keith the full 60.

  • Juan pays NZD 40 for a shared taxi → Keith owes Juan 20.


Correct net: Juan owes Keith 50 + 60 − 20 = 90. The original logic only split the shared lines, missed the middle case, and undercounted what was owed.


The fix
I re-modelled every expense as a pair: who paid and who it's for (individual-to-a-person, or shared 50/50) and computed the net balance from payer versus beneficiary, rather than from shared splits alone. I then updated the code to reflect the corrected logic.

Screen recording of bill split

Why this matters for a BA

This is the full requirements lifecycle in miniature and it puts two core BA skills on show: documentation (test cases and a defect record) and UAT (validate against real use, catch a flawed assumption, log it, fix, re-test). Most projects can't demonstrate this loop because they were never actually used — this one was.

Copyright © Website design & Content by Chew Lijuan

Interested in working together? Drop me a line

Copy Email

  • Available for Work

    Get in Touch

    Available for Work

    Get in Touch

    Available for Work

    Get in Touch

  • Available for Work

    Get in Touch