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

Role-Based Access Control for Enterprise Communities

CompanyGainsight
RoleProduct 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.

2 → 1
Architecture debate settled
Resolved direct-vs-permission-sets across engineering and product before a sprint was scoped
3 groups
Aligned on one data model
Engineering, product, and customer success, agreed in prototype, not in production
9
Prototype versions
Each driven by a concrete gap from stakeholder review
01
The Problem

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

The core issue was not missing features, it was that the data model could not represent scope. A role bundled permissions, identity, and where-it-applies into one object. That is a design problem masquerading as an engineering one.

Roles per area (current model)
0
Scoped assignments supported
3
Pain points driving the project

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.

Key points
01Role explosion: "Editor" becomes "Editor-EMEA", "Editor-KB", "Editor-CS", each with its own diverging permission set
02No scope control: a role applies platform-wide regardless of where a user actually operates
03Over-provisioning as the path of least resistance, admins granted broad access rather than configure properly
04Khoros parity requirement: migrating enterprise customers expected role granularity the current system could not provide
Configuring one role, current vs redesigned
Current system
Go to role page
Choose scope/access
Select KB or Community page
Select section or category
Define permissions for that area
↻ repeat for every area, one at a time
Redesigned
Go to role page
Define permissions once, grouped
Define scope once, via the section tree

The current system repeats the same sequence once per area an admin wants to configure. The redesign collapses that into two one-time steps regardless of how many areas are involved.

The redesigned roles list: system and custom roles grouped, searchable, with a compare actionAfter
The legacy roles list: a flat table with no grouping, no scope, and no way to compare rolesBefore
◀ ▶

The roles list, before and after · drag to compare

02
Architecture Decision

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

The most consequential decision was not a visual one. Whether to put scope on the role, on the assignment, or as an independent object determined what every screen in the system had to do.

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.

Approaches explored
1
Scope bundled into the role definition. Simple to explain, but scope is user-specific, recreates role explosion at a different level.
2
No roles, bundle permissions directly onto each assignment with a scope. Maximum flexibility, but shared behaviour becomes impossible to update.
3
Role = what. Scope = where. Assignment = who does what where. All three are independent and reusable. A role change propagates to every user holding it immediately.
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
The assign-scope step: Full access vs Specific areas, with a section tree for Community and Knowledge Base
03
The Backend Architecture Debate

When the engineering model and the UX pull in opposite directions

This was the highest-stakes decision in the project. The permission sets model was architecturally elegant but would have made the admin experience meaningfully worse. Making that case required evidence, not opinion.

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.

Key points
01Sets model: atomic bulk updates, structural drift prevention, but introduces a fourth concept and hides what permissions a role actually grants
02Direct model: every permission visible on the role, three concepts not four, faster to build and easier to audit
03SQL performance: negligible difference at CC scale, performance was not the deciding factor
04Industry precedent: Discord, Slack, Linear, Notion, GitHub, Discourse all direct, sets only appear at AWS/Salesforce infrastructure scale
05Drift mitigation via tooling (role compare + diff view), not architecture, adding abstraction trades one problem for three others
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.
The users tab for a role, showing exactly what each person has: Full access or Specific areas, visible without opening anything
04
Prototyping with Claude Code

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

Static mockups are proposals. A functional prototype with real interaction logic is evidence. Nine iterations later, review feedback was about experience, not speculation about what it might be.

9
Prototype versions
142kb
Single file, no framework
50+
KB categories encoded

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.

The prototype permission editor: grouped by content type, with live counts and search, scaling to 100+ permissionsAfter
The old permission editor: a flat, ungrouped list of access toggles with no counts and no structureBefore
◀ ▶

The permission editor, before and after nine iterations · drag to compare

rbac-prototype · v9Built with Claude Code

Interactive prototype available on request

The full nine-version prototype runs real data, state, and edge cases. It is kept private until the feature ships to production. Happy to walk through it live or share access on request.

Request access →
05
Key UX Decisions

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

The most important UX decisions here were not about visual polish. They were about reducing the number of concepts an admin needs to hold in their head at any point.

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.

"Every decision reduced the number of concepts an admin needs to hold in mind at once, the accordion over a toggle, scope groups over a flat list, radio cards over an ambiguous toggle."
The complete role-creation flow
Role creation flow, including optional description, scope, and bulk-assignment branchesStep 1, Role definitionStep 2, PermissionsStep 3, Scope (optional)Step 4, Assign users (optional)StartpointGo to role pageOpen modal(Create role)Assign a nameAssign adescription?YesDefinedescriptionChoose roledetailsNoClickContinueChoose permissions or apermission set for this roleClickContinueSelect the areas of theplatform this role can useChoose modules, sections,subsections, or categoriesClickContinueAssign rolesin bulk?YesAssign roleto a segmentSystemAssign roleto usersNoClick"Create role"

Description and scope are skippable, and roles can be assigned to a segment in bulk or to individual users directly. Every optional path still converges back to a single "Create role" action.

06
Status & Reflection

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

The best decision in this project was not a visual one. It was choosing to co-own the architecture problem with engineering rather than waiting for a spec to design against.

The permission grouping is finalised. The flat permissions architecture is agreed across design and engineering, and the model is now in active development with a beta release planned. The prototype reached 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.

The most telling signal came from engineering. The backend lead told me that bringing design into the problem surfaced user constraints and workflow pain points the team had not caught while evaluating it from a purely functional and technical angle, gaps that would otherwise have shipped as friction and been expensive to unwind later. The architecture they were about to commit to changed because of it.

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.

Key points
01Settled the direct-vs-permission-sets architecture debate with a six-criteria evaluation, across engineering, product, and customer success
02Engineering lead credited the work with surfacing user constraints and workflow pain points the team had missed evaluating it from a tech-only angle
03Now in active development with a beta release planned, architecture agreed, data model reviewed
04Co-owning the architecture problem with engineering produced a more defensible solution than designing against a spec
Outcome

Resolved a contested backend architecture debate, direct vs. permission-sets, across engineering and product before a sprint was scoped on the wrong model. Now in active development, beta planned.

Team

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

Next project

Thread Replies & Communities

Designed the first threaded reply system for Gainsight Customer Communities, transforming flat comment feeds into structured discussions. A contractual requirement in enterprise deals and a direct factor in lost contracts.

Read case study →
Thread Replies & Communities