01/ 11
← Work/02
Admin UXSystems DesignAI PrototypingEnterpriseB2B SaaS·10 min read

Role-Based Access Control for Enterprise Communities

CompanyGainsight
RoleSenior Product Designer
Year2026

The existing permission system had no concept of scope — an Editor was an Editor everywhere, forcing admins into role explosion or over-provisioning. I designed a new RBAC architecture, co-designed the backend data model with engineering, resolved a high-stakes architecture debate between two competing backend approaches, and built nine functional prototype versions with Claude Code to drive alignment before a single sprint was scoped.

Business Context

This project is still in progress — the prototype has completed a full stakeholder review cycle and the architecture is agreed across design and engineering. It is included here because the process is the story: a complex systems problem that required working simultaneously across UX, data modelling, and backend architecture to arrive at a coherent solution.

Role-Based Access Control for Enterprise Communities
9
Prototype versions across the project
142kb
Standalone prototype, no framework
6
Design changes from one review cycle
01 — The Problem

A permission system that could not represent how enterprise teams actually work

Gainsight Communities serves enterprise customers running large, structured online communities — knowledge bases, Q&A forums, ideas boards, and moderated discussion. As customers scale, a recurring problem has surfaced: the current permission system cannot represent how real community teams are structured.

A large customer might have a dedicated KB author team, a separate Q&A moderation team, and regional managers who each own different community sections. The current system cannot model this cleanly. An Editor is an Editor everywhere — admins either over-provision access or create an explosion of near-identical roles (Editor-EMEA, Editor-KB, Editor-CS) that diverge over time and become impossible to maintain.

A second pressure: Khoros parity. Enterprise customers migrating from Khoros expect comparable role granularity. The gap was appearing on contract checklists and in sales conversations.

RBAC roles overview — roles list with permissions grouped by content type
02 — Architecture Decision

Separating what from where — and why the model mattered more than the interface

I explored three structural directions before recommending one.

Approach A — All on the role. A role bundles both permissions and scope. Simple to explain, but scope is user-specific — encoding it into the role recreates role explosion at a different level.

Approach B — All on the assignment. No roles — just assign a permission bundle directly to a user with a scope. Maximum flexibility, but updating shared behaviour means touching every individual assignment.

Approach C (chosen) — Separated and independently reusable. Roles define permissions only. Scope is a separate per-assignment configuration. A user can hold multiple assignments: a role with full access globally, plus a second role scoped to one community area. A role change propagates to all users holding it immediately. Scope changes affect only that assignment.

This separation also shaped the backend data model — six tables designed alongside the UI: roles, permissions, role_permissions, assignments, assignment_scopes, and the three-level community hierarchy. The runtime permission check is a three-step query: load assignments for the user, check role_permissions for the requested permission key, then verify the resource falls within assignment_scopes if scoped.

Architecture comparisonRBAC architecture comparisonThree approaches to permissions and scope: A bundles scope into role causing explosion, B puts scope on assignment causing manual overhead, C separates them as independent reusable objectsOption A — Scope on roleOption B — Scope on assignmentOption C — ChosenEditor — EMEApermissions + EMEA scopeEditor — APACpermissions + APAC scopeEditor — KBpermissions + KB scopeProblem: role explosionAnna: Editor + EMEABen: Editor + APACChloe: Editor + KBProblem: changing a permissionmeans updating every userRole: Editorpermissions only — reusableAccess Set: EMEAscope only — reusableAssignmentUser + Role + Access SetChange role → all users updatedMaintenance cost as platform growsN × areasroles multiplyN × usersupdates multiplyflat alwaysA and B both explode at scale · C stays constant regardless of team or area size
RBAC users tab — scope groups showing the same role assigned with different scopes
03 — The Backend Architecture Debate

When the engineering model and the UX pull in opposite directions

How individual permissions are stored and assigned to a role was the most contested decision in the project. Two engineering positions emerged.

The permission sets model: permissions are grouped into named, reusable sets. Roles are assigned sets, not individual permissions directly. This allows bulk updates — change one set and every role using it updates atomically. It also creates structural drift prevention by design.

The direct/flat model: permissions are assigned directly to roles. A role is a named list of permissions — nothing more. Simpler to reason about, and significantly simpler to build an interface for.

I led a structured evaluation across six criteria: UI complexity, bulk update behaviour, permission visibility, drift risk, SQL performance, and industry precedent. The sets model introduced a fourth concept (sets, alongside roles, users, and assignments) and hid individual permissions behind set names — admins could not see what they were actually granting without navigating to the set definition. The drift risk the sets model solved had not been observed in over ten years of enterprise operation on comparable platforms.

SQL performance analysis showed negligible difference between models at CC scale. Industry benchmarking confirmed the direct model as the standard across every comparable platform at this scale: Discord (47 permissions, assigned directly), Slack, Linear, Notion, GitHub, Intercom, and Discourse all use direct. Permission sets only appear at infrastructure scale — AWS IAM, Salesforce — where hundreds of permissions across thousands of services make an intermediate layer genuinely necessary. The drift mitigation for our context is a role compare view and role duplication as a first-class feature — not an architectural abstraction.

Model comparison
CriterionPermission sets model
Direct / flat modelChosen
Mental model
4 concepts: sets, roles, users, assignments
3 concepts: roles, users, assignments
Permission visibility
Hidden behind set name — admin cannot see what a role grants without opening the set
Fully visible on the role — every permission listed directly
Bulk update
Atomic: change one set, all roles update instantly
Update N roles — mitigated by role duplication and compare view
Drift prevention
Structural — architecture enforces consistency
Tooling-based — compare + diff view surfaces divergence
Industry precedent
AWS IAM, Salesforce — infrastructure scale with hundreds of permissions
Discord, Slack, Linear, Notion, GitHub, Discourse — every comparable platform
DecisionDirect wins on UX simplicity, permission visibility, and industry precedent. Sets wins on bulk update and structural drift prevention — both mitigated by tooling at this scale. Three problems added to solve one.
RBAC scope assignment modal — Global access vs Specific areas with community tree
04 — Prototyping with Claude Code

Nine versions, a working data model, and feedback that could only come from using the real thing

Complex permission systems are notoriously hard to evaluate in static form. Stakeholders nod at wireframes, then hit interaction edge cases the moment they use the actual thing. For a system with accordion state, three-state checkboxes, scope tree navigation, and role comparison, I needed something people could actually operate.

I used Claude Code to build a fully interactive HTML prototype — nine major versions across the project. The loop was direct: describe the interaction model, generate working HTML and JavaScript, review in the browser, catch the edge case, revise. What would typically take a developer two or three sprints took hours.

The prototype is a single standalone file with no framework dependencies, using exact design tokens from the CC Design System. The community structure is fully encoded — the Knowledge Base alone has 50+ categories. It implements real data structures, real state transitions, and real edge case handling: three-state checkboxes that cascade correctly through a hierarchy, scope trees that filter and auto-expand matching branches, and a role comparison modal that scales to 100+ roles without becoming unworkable.

RBAC compare roles modal — side-by-side permission table with differences toggle
05 — Key UX Decisions

The interaction decisions that changed most between v1 and the final model

The accordion over a simple/advanced toggle. The original prototype had a simple/advanced mode toggle on the permissions tab. This imposed a "beginner vs expert" framing that required the admin to make a meta-decision before doing any actual work. The accordion collapses that entirely: sections are compressed by default, a single checkbox selects all without expanding, and detail is revealed on demand. No mode — just progressive disclosure.

The section-level checkbox on the collapsed header. A review catch: the original design required expanding a section to access the select-all button — an unnecessary click for the most common action (enabling an entire content type for a focused role). Moving the checkbox to the collapsed header means you can configure a full role without expanding a single section.

Scope groups in the users tab. The original users tab was a flat list. This broke down for enterprise accounts where the same role might be assigned to different people with different scopes — a KB author scoped to Product Documentation vs one scoped to Release Notes. Scope groups group users by their shared scope, make the scope visible on the card header, and provide separate "Add users" and "Edit scope" actions. Creating a new scoped assignment is a first-class action, not an afterthought.

Access level radio cards over a toggle. The scope tab originally used a toggle labelled "Global access". It was unclear whether the state meant "currently global" or "make global". The replacement: two explicit radio cards — Full access (with a green confirmation banner describing exactly what it covers) and Specific areas (which immediately reveals the tree). Intent is unambiguous at a glance.

06 — Status & Reflection

What is agreed, what remains open, and what this project demonstrates

The permission grouping is finalised. The flat permissions architecture is agreed across design and engineering. The prototype is at version 9 and covers the full flow from role creation to user assignment with scope groups. The backend data model has been reviewed. The role compare modal is built and iterated.

Three questions remain open: whether MCP scope shares the role_permissions table or runs independently (this affects the scope tab design for MCP-enabled roles), when the role compare and diff view ships (built in prototype, needs PM sign-off), and customer validation of the six role templates in the new role wizard before hardcoding.

Three things this project makes concrete. I push hard on the underlying model before touching interface — the architecture debate was design work that determined what every screen had to do. I treat prototyping as a method, not a deliverable — the prototype existed to generate feedback, surface edge cases, and resolve architecture uncertainty faster than written specs could. And I work across UX, systems design, and implementation — understanding the data structures well enough to co-design them is what made the architecture debate resolvable with evidence rather than opinion.

Prototype review session — stakeholder walkthrough, available on request

Available on request

Team

Senior Product Designer (solo), Engineering Lead, Senior PM, Customer Success stakeholders.

Next project

Brand Protection Platform