02 / iOS app2026

Tournify

Running a local tournament usually means a form for signups, a bracket site, a whiteboard for scores and a group chat for everything else. Tournify is those four things in one place, built so that two captains reporting the same match at the same time cannot fork the bracket.Match reporting is an idempotent edge function that checks a version on the bracket row, so a duplicate or concurrent submit collapses instead of forking the tree. Standings arrive over realtime channels rather than a poll.

03 / iOS app2025

Tiny Challenges

Everyone gets the same three prompts at midnight. You cannot see what a friend posted until you have posted your own, and that rule lives in the database rather than the app, so there is nothing to work around.Visibility is a row level security policy: selecting a friend's entry requires an existing entry from you for the same prompt and date. The client is never trusted with the gate, so a patched build still sees nothing extra.

04 / Website2024

Coffee Digest

Changing a grind size writes a new entry instead of overwriting the old one, so the log ends up as a record of what you tried rather than what you last thought. It is a website, because dialling in a grinder happens at a desk.Append only event table with a materialised view for the current state of each brew. An edit is a new event, so history is never lost and the read path stays a single query.