Roles are often the first abstraction people reach for when designing authorization. They're intuitive, human-readable, and easy to explain: admins, editors, viewers. For small systems, that's usually enough.
But as systems grow, roles tend to accumulate meaning. They stop being labels and start becoming containers for behavior. At that point, they quietly shift from a convenience into a source of complexity.
Understanding the difference between roles and capabilities - and how they relate - is one of the most useful conceptual shifts you can make when designing authorization.
In this context, "roles" refers to roles that are embedded directly into application logic - where behavior is gated by checks like if (user.role == "Admin"). At that point, roles aren't just labels or configuration anymore; they become part of the system's control flow. That's different from roles as a higher-level organizational construct that simply group permissions. The distinction matters, because the problems discussed here arise when roles are treated as logic rather than as configuration.
Why roles feel so natural
Roles map cleanly to how people talk about access. You can say "this user is an admin" and everyone has a rough idea of what that implies. They're easy to name, easy to assign, and easy to reason about early on.
In small systems, this works well. A role becomes a bundle of permissions, and authorization becomes a matter of checking whether a user has the right role.
The trouble begins when that bundle needs to change.
When roles start to stretch
As systems evolve, roles begin to accumulate exceptions.
Someone needs most of what an admin can do, but not everything. Another user needs admin access only in a specific context. Someone else needs temporary access, or read-only access to a narrow slice of the system.
At that point, roles start to fragment:
Admin becomes AdminWithoutBilling, RegionalAdmin, LimitedAdmin, TemporaryAdmin, and so on.
Each new variant encodes business rules directly into the role name. Over time, roles stop being conceptual groupings and start acting like policy definitions. The system still works, but understanding it now depends on tribal knowledge rather than reasoning.
This is usually the point where authorization begins to feel brittle.
Capabilities as a more stable unit
A useful way out of this is to separate what someone can do from how those abilities are grouped.
Capabilities describe individual actions in the system:
- viewing a record
- editing a resource
- approving a change
- initiating a workflow
They are small, explicit, and usually stable over time. New features may introduce new capabilities, but existing ones rarely change meaning.
Roles, in contrast, become mappings over those capabilities. They exist to group capabilities in ways that make sense for people and organizations.
This separation lets each concept evolve at its own pace:
- capabilities evolve with features
- roles evolve with organizational structure
Neither needs to absorb the complexity of the other.
Why this distinction matters in practice
When roles directly encode behavior, changing behavior means changing role definitions everywhere they're referenced. That often leads to cautious systems where even small policy changes feel risky.
When capabilities are treated as the primitive instead, roles become configuration rather than logic. You can adjust which capabilities belong to a role without rewriting authorization rules or touching application code.
It also makes intent clearer. A check like "can approve invoices" communicates meaning directly, while a role name usually requires extra context to interpret correctly.
Over time, this clarity compounds. Authorization rules become easier to reason about, easier to test, and easier to explain to people who didn't design the system.
Roles still matter - just differently
None of this means roles are obsolete. They remain useful as a human-facing abstraction.
People think in roles. Organizations assign roles. Admin interfaces are easier to build around roles than around raw capabilities. That part doesn't change.
What does change is where behavior lives. Roles shouldn't define what actions are allowed; they should simply reference the capabilities a person has. Authorization decisions then operate on those capabilities, not on role names themselves.
This keeps roles stable even as behavior evolves, and avoids the steady growth of special-case roles that tends to appear over time.
A quieter form of flexibility
One benefit of capability-based thinking is that flexibility emerges naturally.
Temporary access, scoped access, delegated access, or context-sensitive rules don't require inventing new roles. They fall out of how capabilities are evaluated in context.
That flexibility doesn't come from adding complexity. It comes from choosing the right level of abstraction.
The short version
Roles are a useful organizational tool, but they make a poor foundation for expressing behavior.
Capabilities provide a more stable vocabulary for describing what actions are allowed, while roles remain a convenient way to group those capabilities for people. Keeping those responsibilities separate makes authorization systems easier to understand, easier to change, and far more resilient as requirements evolve.