Why this app exists
The first number flatters. The second one cancels the purchase.
A monthly income is an abstraction: a number that shows up once a month and disappears again. Flowi puts it back in motion and shows it arriving second by second.
But the idea that matters is the reverse conversion: a price becomes a duration of work. And that duration is only honest if fixed costs are in the equation. Measured against gross income, a purchase showed up as a few days of work, ignoring that the rent had already gone. With the fixed costs entered, the same purchase weighs a full month of what is actually left to live on.
The rest follows: no account to create, no data leaving the device, and an opening flow that asks for the minimum before showing something useful.
The engineering challenges
Thirty-eight passing tests, and the app died on launch.
The crash the tests could not see. The app died on the first screen. The embedded JavaScript engine does not implement the detailed number-formatting call, and the formatter is the very first line the home screen runs, so the crash landed before anything was drawn. The instructive part is why the test suite never flagged it: the tests run on Node, which ships the full international data set, while the app runs on a slimmed-down engine that does not. Formatting is now derived by formatting a known number and reading the result back, using the one entry point that is genuinely available, and two regression tests lock the behaviour down, one of which makes the missing function fail on purpose.
A live counter that does not drain the battery. The figures refresh ten times a second, but the timer is torn down as soon as the app leaves the foreground and a fresh value is computed on return. The amounts are pure functions of elapsed time: there is nothing to catch up on, and a timer still ticking behind a locked screen would only burn power.
Fixed costs, modelled separately and never as negative income. That was the obvious shortcut, and it would have corrupted every rate calculation: the app needs both totals side by side, not a net balance. A dozen tests cover the negative disposable figure that must not be clamped, division by zero, share of gross versus share of disposable, and the zero price that must never produce an invalid value.
Translating turned out to mean formatting. The trap was not the words but the format, hard-wired to one locale in a dozen places: an English interface printing a French-formatted amount would have given away machine translation at a glance. Number grouping, currency symbol position, time notation and even the decimal separator in the odometer, which prints the integer and decimal parts separately, now follow the language. The English catalogue is typed against the French one, so a missing key breaks the build instead of surfacing in production.
Optical alignment of the amount. The currency symbol visually pushed the number off centre by a good fifteen pixels. The fix is an invisible twin of the symbol placed on the left, hidden from screen readers: a hard-coded offset would have been right on one device and wrong on the next.