Skip to main content

What building this taught me

A few real takeaways from shipping a full-stack household finance app solo.

RLS does more than app code ever could

Household isolation lives in Postgres policies, not if statements: a bug in a Server Action can't leak another household's data.

Hand-rolled validators, for now

Zod would be a natural upgrade, but installing a new dependency couldn't be verified in the environment this was built in, so hand-rolled validators cover the same cases in the meantime.

Two flows RLS can't express

Accepting an invite before membership exists, and deleting a household as its last member, both go through SECURITY DEFINER functions instead.

A CI budget catches what code review misses

A bundle-size gate in CI has caught regressions that lint and type-checking never would have.

Idempotency needs a database precondition

Two concurrent "mark as paid" clicks both read the same stale row and both wrote, fixed with a single conditional UPDATE instead of a faster read-then-write.

Logged in isn't the same as verified

The password-reset page is gated on a short-lived, single-use cookie set at code-exchange time; without it, any signed-in session could reach it and change a password with no current-password check.

Two destructive actions, two blast radii

Wiping your data and deleting your account needed separate flows: one keeps your login and household intact, the other removes both.

Editing history means closing a row, not rewriting it

Changing a recurring entry's amount doesn't touch its past rows. It closes the old one out and starts a new one, so a March rent increase never quietly changes what February looked like.