CASE STUDY 02 · SaaS

EMS

Multi-tenant HR for Indian companies, payroll compliance included.

EMS landing page: Simplify HR. Automate Compliance. Empower Teams.

Handles the full employee lifecycle — onboarding, shift-based attendance, leave, payroll with Indian statutory compliance (PF, ESI, PT, TDS), and OKR performance reviews.

4

statutory regimes implemented (PF, ESI, PT, TDS)

full lifecycle

onboarding → attendance → payroll → reviews

multi-tenant

one deployment, row-isolated companies

THE PROBLEM

Indian SMEs live in a gap: global HR SaaS doesn't understand PF, ESI, professional tax, or TDS, and the local options are desktop-era software. Most companies end up running payroll in spreadsheets that one person understands.

Payroll is unforgiving territory — a rounding error or a mis-slabbed tax isn't a bug, it's someone's salary.

WHAT I BUILT

A multi-tenant HR platform covering the lifecycle end-to-end: onboarding, shift-based attendance with configurable policies, leave accrual and approval chains, payroll runs that produce compliant payslips, and OKR-based performance reviews.

The payroll engine implements Indian statutory logic — PF and ESI contributions, state-wise professional tax slabs, TDS projection across the financial year — as versioned, testable rules rather than spreadsheet formulas.

ARCHITECTURE

Lit 3 web components (SPA)
      │ REST
      ▼
Fastify 5 API ── multi-tenant guard (tenant_id scoping)
      │
      ├─ payroll engine (versioned statutory rules)
      ├─ attendance/shift scheduler
      └─ Drizzle ORM ──► PostgreSQL (per-tenant row isolation)
  • Tenancy is row-level: every query path goes through a tenant guard, so one database serves all companies without cross-tenant leakage.
  • Statutory rules (PF caps, ESI thresholds, PT slabs per state, TDS regime) are data with effective dates, not hardcoded constants — when the rules change in a budget year, old payslips stay reproducible.
  • Payroll runs are idempotent and auditable: a run snapshots its inputs, so re-running never silently changes issued payslips.

STACK — AND WHY

Lit 3

Web components keep the front end framework-agnostic and small — the same bet as Pulse, shared muscle memory.

Fastify 5

Fast, schema-validated API layer; JSON schema validation catches malformed payroll inputs at the boundary.

PostgreSQL + Drizzle

Relational integrity for money and attendance data; typed queries so payroll math is checked end-to-end.

Docker

One container per service on the VPS, deployed behind Caddy.

THE HARD PARTS

Statutory correctness with a straight face

PF has wage ceilings, ESI has eligibility thresholds that flip mid-year, professional tax differs by state, and TDS depends on a projected annual income that changes every time salary changes. Encoding these as effective-dated rule tables — and testing them against worked examples — was the bulk of the engineering.

Shift-based attendance without edge-case hell

Night shifts cross date boundaries, half-days interact with leave accrual, and late-mark policies vary per company. The attendance model had to be policy-driven configuration, not code branches per customer.

Multi-tenancy you can trust with salaries

Row-level tenancy is easy to claim and easy to get wrong. Every data access path is forced through a tenant-scoped query layer — there is no way to write an unscoped query without deliberately bypassing the ORM wrapper.

WHAT IT TAUGHT ME

  • Compliance logic is a data-modeling problem. The moment tax rules became effective-dated rows instead of constants, yearly rule changes stopped being rewrites.
  • For money, idempotency beats cleverness — snapshot inputs, make runs reproducible, never mutate an issued payslip.
  • Building for Indian SMEs specifically (rather than 'HR for everyone') is what made the product coherent.
Visit ems.hire.rest← Back to the descent