Engineering Leadership & Technical HiringPlaybook3 min readUpdated September 2026

Designing Role-Based Access Control That Scales With You

Access control usually starts as a single boolean, is this user an admin, and stays that way long after the company has customer support staff, contractors, and multiple customer-facing roles that all need different slices of the same system. Retrofitting real role-based access control onto a codebase full of scattered admin checks is far more expensive than designing it with a little more structure from the start.

This is a practical starting point for a small engineering team building or rebuilding access control, without over-engineering a permissions system nobody in your position needs yet.

Should you start with roles or per-action permissions?

A permission-per-action model looks flexible on a whiteboard and becomes unmanageable once you have thirty of them and nobody remembers what half of them actually gate. Start with a small number of roles, owner, admin, member, and read-only viewer covers most early-stage products, and add a new role only when two existing roles genuinely need different access to the same feature.

Resist the urge to build a fully custom permission builder for customers before you have more than a couple of customers actually asking for it. That flexibility has real engineering cost, and most companies that build it early end up with only two or three permission combinations actually in use, which means most of that flexibility sits unused while still needing to be tested and maintained.

Where should the permission check live in your code?

The most common RBAC failure is not a missing role, it is a permission check that lives inline in a dozen different route handlers, so a new endpoint ships without one because nobody remembered the pattern. Centralize the check, a single middleware function or a decorator every protected route uses, so adding a new endpoint means opting into the pattern automatically instead of remembering to copy it.

Write an automated test that walks every route and confirms it has an access check attached. That single test catches the exact mistake that shows up in incident postmortems most often: a new feature shipped reachable by anyone with a valid session, regardless of role.

Decide where roles live: the org, the resource, or both

Most products need roles at two levels, a company-wide role like admin or member, and a resource-level role for anything shared selectively, like a project or a workspace a user was invited into but does not own. Conflating the two is where things get confusing: an org admin should usually see everything, but a project member should not automatically become an org admin just because they were invited to one project.

Model this explicitly in your data layer from the start, even if only the org-level role exists today. Adding resource-level permissions later, without that structure already in place, usually means a painful migration through every table that currently assumes a single global role.

For example, a user is invited to a single project in a customer's workspace. At the organization level they are a member, and at the project level they are an editor. An org admin sees everything, but this user should see only that project. If your data model has just one global role, you must choose between giving them too much access or building an exception for every invited user. Store the organization role and the resource role separately from day one, even if only the first is used today. Then adding project-level sharing later means adding rows, not rewriting every table.

Audit who actually has access, on a schedule

Roles drift. A contractor's access outlives the contract, a former employee's API token still works because nobody built an offboarding checklist, and an admin role granted for a one-time task never gets revoked. None of this shows up in code review, because the code is working exactly as designed.

Run a quarterly access review: pull every user with elevated access, confirm each one still needs it, and revoke what nobody can justify. This is one of the cheapest security practices available to a small team relative to what it catches, and it costs an afternoon a quarter.

Tie offboarding directly into this review instead of treating it as a separate HR process. The most common gap is not a missing policy, it is a policy that exists on paper but has no trigger connecting an employee's departure to an actual revocation of their access across every system they touched, from the codebase down to a third-party dashboard nobody remembered to include.

Run the quarterly access review in this order:

  1. Pull a list of every user, contractor and API token that currently has elevated access.
  2. Ask an owner to confirm that each one still needs that access, and revoke anything nobody can justify.
  3. Check that every departure since the last review actually triggered revocation across every system, including third-party dashboards.
  4. Record the result, and connect offboarding to a trigger so revocation does not depend on someone remembering.
Executive Capability Standard

What Good Looks Like

Good here means a new route cannot ship without an access check, because the check happens in one centralized place that every route passes through, not because every engineer remembered to add one.

Building The Capability (5-Stage Skill Ladder)

1. Learn:List every role your product currently has, even informal ones scattered through code as boolean flags, and note what each one can actually access.
2. Do Manually:Manually review every route or endpoint against your role list and flag any that are missing an explicit access check.
3. Delegate:Assign an engineer to centralize permission checks into a single middleware or decorator pattern that new routes must use by default.
4. Automate:Add an automated test that walks every route and fails the build if a protected endpoint is missing an access check.
5. Buy:Bring in a fractional CTO or security-focused contractor to design the role model once if the resource-level permission structure feels bigger than your team has designed before.

How to Get Started

Frequently Asked Questions

How many roles should a small product start with?

Three or four is plenty for most early-stage products: an owner, an admin, a standard member, and often a read-only viewer. Add a new role only when you can point to a specific feature that two existing roles genuinely need different access to, not in anticipation of a future customer request.

Should permissions be hardcoded or configurable per customer?

Hardcoded roles are the right default until you have real customers asking for custom combinations, not before. A configurable permission system is meaningfully more code to build, test, and secure, and most companies that build it early end up with only a handful of the possible combinations ever actually used.

What is the most common access control mistake small teams make?

Scattering permission checks inline across individual route handlers instead of centralizing them in one place. That pattern makes it easy for a new feature to ship without any access check at all, simply because nobody remembered to add one, and it is the single most common root cause behind an accidental data exposure.

About the numbers

This guide doesn't quote a sourced benchmark. Figures in it are estimates or general guidance, so check them against your own numbers.

Related Guides