Syed Kazmi
Colour theme

← Selected work

sblpro

Role
Solo, personal project
Period
2025 to present
Outcome
A 12-table, 13-migration schema with row-level security and 95% test coverage, live in production
Stack
  • TypeScript
  • React
  • IndexedDB
  • PostgreSQL
  • Supabase
Links

Problem

Build a strength training tracker where logging a set at the gym never waits on a connection, and where adding accounts later would not force existing users to lose the offline history they already had.

Constraints

  • Solo project, so the offline write path, the sync path, and the account system all had to be designed and maintained by one person.
  • Existing users already had workouts stored locally before accounts existed. Adding accounts could not silently discard or merge that data.
  • The same workout can be edited on two devices before either syncs, so conflicts are not an edge case, they are a normal path.

What I built

Local-first writes. Every workout saves to the browser’s IndexedDB first, so logging a set never blocks on the network, then syncs to Supabase in the background once a connection is available.

A migration path for existing local data. When a device with offline history gets an account attached, that history moves onto the account rather than being dropped, and if the device already belongs to someone else the app asks rather than merging silently.

Row-level security across a 12-table, 13-migration schema. Access control lives in PostgreSQL itself, enforced per row, rather than in application code that has to remember to filter correctly on every query.

95% test coverage and 60+ automated access checks run against a real database as part of the build pipeline, plus a dedicated bug-hunting pass before release that caught a missing permission check on a feature that erases user data.

Decisions and tradeoffs

Conflict resolution decided by the database, not the client. Early on, the client could decide which version of an edited workout won, which meant a slow sync from one device could silently discard a newer edit made on another. Moving that decision into the database, so the server always has the final say, closed that bug at the cost of a sync path that is harder to reason about than a client simply overwriting on save.

Row-level security over application-level checks. Enforcing access in Postgres rather than in the app is more upfront schema work and makes local development against the real database slower to set up. It also means a bug in the application layer cannot leak another user’s data, which is the property that matters more once accounts exist at all.

All projectsEmail me about this