P-06 · BUILD / PROJECT · 2026 SHIPPED

EasyShifts

Multi-tenant shift scheduling and payroll, built for and used by a boutique fitness studio in Singapore.

STACK: TypeScriptWebAuthnAES-256-GCMPlaywrightHeadless Chromium

What it is

Shift scheduling and month-end payroll for small businesses that run on shifts, built for a real client (a boutique fitness studio in Singapore) and running in their production. Coaches submit availability, the system builds the roster, the owner reviews and publishes it, and at month end the pay is computed from what was actually published.

Having a client rather than a hypothesis changed the product. Half the features I assumed were essential were never asked for, and the one thing that mattered most was something no generic scheduler does.

The opinion in the engine

The scheduling engine is pure TypeScript, and it holds a deliberate opinion: contiguity beats fairness. Given a choice between splitting hours evenly across four coaches and keeping one coach on-site for a solid block, it keeps the block. On paper the even split is fairer. In a studio it means four handovers, four sets of keys, and nobody who knows what happened that afternoon.

That’s a business rule, not an algorithmic preference, so it lives somewhere it can be read and argued with rather than buried in a scoring function.

A draft week, blocks kept whole per coach, backups where cover is thin

Details I’d point at

Publishing renders the roster to PDF or JPG through headless Chromium, because the roster’s real distribution channel is a group chat, not a login page.

Sign-in is WebAuthn passkeys with magic links as the fallback. Bank and PayNow identifiers are encrypted at rest with AES-256-GCM. Tenant isolation is strict and audit logging is on throughout. This is payroll, and payroll disputes are settled by records.

The notification layer is adapters: email, WhatsApp, Telegram, push. All of them fall back to a mock, which means the entire product demos end to end with zero API keys configured. Handing someone a working demo shouldn’t require handing them a bill.

Payroll follows the same principle as the roster: pay rules are data, not code. There is no hardcoded 1.5× anywhere. When the rate for a public holiday changes, somebody edits a row, not a deployment. A run drafts itself from the periods that were actually published, so the pay and the schedule can never drift apart.

Month-end payroll, drafted from the periods that were actually published

Where it stands

Roughly 70,000 lines, about 1,300 automated tests plus Playwright end-to-end coverage, shipped to production in around two weeks of evenings. In live use.