Permissions Are a Product Decision
Most permission systems start the same way: there are admins and there are users, admins can do everything, users can do the obvious things, and that’s enough to ship. It’s a reasonable beginning. What makes it interesting is what happens next, because the second customer has a person who needs to approve things but not create them, and the third has a contractor who should see one project and nothing else, and each accommodation gets bolted on as a special case until nobody can answer “who can see this?” without reading code.
The reason it degrades is that the initial model wasn’t a model — it was a guess about organizational structure, made early, by people thinking about the software rather than the customer. Real permission requirements come from how the buying organization actually works: who is accountable for what, which actions need a second pair of eyes, what a regulator requires them to be able to prove. Those facts have nothing to do with your architecture and won’t be discovered by thinking harder about your code. They show up in sales calls and support tickets, usually phrased as “can we make it so that…”
Which is why treating this as plumbing is a mistake with a long tail. A permission model that matches how customers describe their own organizations becomes a feature people evaluate you on — enterprise buyers do ask, in detail. One that doesn’t becomes a growing pile of exceptions, each individually reasonable, collectively making the system impossible to reason about. And unlike most accumulating messes, this one has a security dimension: every special case is a chance for the answer to “who can see this?” to be wrong in a direction nobody notices until it matters.
The design question worth asking early isn’t which roles to ship. It’s what the shape of the model is. Are permissions attached to people, or to their position in a structure they already have — a team, a project, an account hierarchy? Is the unit of access a resource, a collection, or an action? Does the customer need to define their own roles, or pick from yours? Those choices are hard to change later because customers configure against them, and the shape is what determines whether the tenth unusual request is a configuration or a code change. Getting the shape approximately right is worth more than getting the initial role list exactly right.
The thing to resist is treating each new access requirement as a one-off. The second time a customer asks for something the model can’t express, that’s information about the model, not just a ticket — and it’s the cheapest moment to reconsider the shape, long before there are enough exceptions that changing it means migrating everyone. Permissions look like infrastructure and behave like product: shaped by customers, evaluated during sales, and expensive to redesign once anyone depends on the current answer.