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

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.