Duo

A Budgeting App for Two

Personal project in development: a mobile app where a couple or a household keeps its budget together, as a cooperative game. Product design, Expo app and Supabase backend.

Role
Design and Development - Personal Project
Period
September 2026 – Present
  • React Native
  • Expo
  • TypeScript
  • Reanimated
  • Skia
  • TanStack Query
  • Supabase
  • PostgreSQL
Duo

Context

Duo is a personal project: a mobile app for keeping a budget as a pair. Every expense logged moves the household forward, with a shared streak, quests and a creature that grows. Entry is manual and fast, with no bank link, so it suits cash and mobile money as well as cards.

Project status. The app is in development and is not yet published on the stores. The name is provisional.

My role

I design and build all of it: the product document, the mobile app and the backend.

What is built

  • Mobile app with Expo and Expo Router: onboarding, sign-in, household creation, quick entry, history, quests, statistics and settings, in French and English.
  • Supabase backend: 8 SQL migrations, row-level security on every table, and 5 Edge Functions (invitations, notifications, subscription, account deletion).
  • QR code invitation to link two accounts into one household.
  • Tests: 19 unit test files on the app side and 199 pgTAP assertions on the database side, covering access rules and game rules.

Going further

I described the path of one entry, from the finger to the server, in an article with an interactive figure (in French): Le trajet d’un tap dans une app React Native.

Decisions and trade-offs

Compute the game on the server, never in the app
Streak, experience and quests are computed by Postgres functions. The app sends an expense and receives a reward. It means SQL functions to test, but a client cannot award itself points, and the streak depends on what the other household member did, which the phone does not reliably know.
Store money as integers, with the household's currency
Amounts are integers in the smallest unit (cents, or CFA francs with no decimals). No floats, on screen, on the network or in the database. Entry adapts: without decimals, the comma key becomes a “000” key.
Generate each expense's identifier on the device
On mobile, a request can fail after it succeeded. Every entry carries a unique identifier created before any network call: a retry never creates a duplicate.
Animate on the UI thread
Touch feedback and animations go through Reanimated and Skia, with no React render per frame. A key responds even when the JavaScript thread is busy. In return, this code is written differently and has to stay small.
Cooperative, never competitive
No leaderboard between household members, no message pointing at who spent too much. The game rewards the habit of logging, never the amount. It is a product rule, written in the repository's guidelines alongside the technical ones.