On a platform with five role types — super admin, institute admin, teacher, student, parent — the tempting shortcut is to hide the buttons each role should not press. It works, right up until someone changes the layout.
Hiding is not enforcing
A hidden button is a design decision. It is not a security boundary. The only place a permission can actually be enforced is on the server, at the point where the data is read or written, and it has to be enforced there whether or not any UI ever calls it.
I keep a single policy layer that answers one question: can this actor perform this action on this resource. The API calls it. The UI calls the same logic to decide what to render. The UI is then a reflection of the rules rather than a second, quietly diverging copy of them.
Scope by resource, not by page
Permissions written as page names age badly, because pages get renamed, merged, and split. Permissions written against resources and verbs survive all of that. A teacher can read the students in their own classes — that sentence is still true after any redesign.
Write permissions against the thing being protected, not the screen it happens to appear on.
The payoff shows up the first time you rebuild a dashboard and the access rules need exactly zero changes. That is the test of whether you got it right.


