Five Roles, One Codebase: RBAC Lessons from a Multi-Tenant EdTech Platform
2026-02-02 · 5 min read
CodeMyFYP Academy serves five very different user roles from the same codebase. The tempting shortcut is to branch on role everywhere — if (user.role === 'recruiter') scattered through the app. That approach rots fast.
Push permission checks to one layer
Instead of scattering role checks through route handlers, permission logic lives at the data-access layer. Every query passes through a policy function that knows what a given role can read or write for a given resource. Routes stay role-agnostic; they just call the same service functions regardless of who's asking.
Data isolation is the real risk, not UI
The UI hiding a button is cosmetic. The dangerous bug is an API that returns another tenant's or another student's data because a query forgot a WHERE clause. Every query that touches student records is scoped by both role and, where relevant, institution — enforced in one shared query-builder rather than reimplemented per endpoint.
Idempotent payments changed how I think about all state transitions
Integrating PhonePe for subscriptions forced idempotency discipline — every payment-state-changing request carries an idempotency key, and webhook handlers are safe to receive the same event twice. That same discipline turned out to be useful everywhere else in the platform: certificate generation, application status changes, and notification sends are all safe to retry.
The AICTE listing raised the bar
Getting CodeMyFYP Academy listed on the AICTE National Internship Portal meant the platform needed to hold up to real institutional scrutiny, not just internal demos. That external validation was the forcing function that took the RBAC model from "good enough for a hackathon" to something I'd trust with real student and recruiter data.