[Investigation] Fields vs. widgets and custom fields
Everyone can contribute. Help move this issue forward while earning points, leveling up and collecting rewards.
Hey everyone
- Fields are everything in the sidebar.
- GitLab-provided fields and custom fields are equal and on the same hierarchy level.
- Features are functionality (based on widgets) that lives in the main section of a work item
Please review the description in this investigation issue. I think it's crucial to align on our high-level approach even before we get into the details of the CWITs GA implementation so we have architectural clarity.
Purpose
This investigation examines whether and how we should evolve from our current widget-based architecture to a fields/features model as we approach field customization work (post-CWITs GA).
This does not require immediate action for GA - we can ship custom work item types with current architecture. However, we should align on direction now so field customization work builds toward a coherent solution rather than accumulating technical debt.
Image: UX vision for configurable fields (configurable work item types iteration 3, directly after CWITs GA).
Background
Current architecture:
- Work item types have widgets
- Widgets serve dual purpose: data holders (assignees, labels) AND functionality (Design Management, Development)
- Custom fields exist as a widget - all custom fields grouped together in the custom fields widget
- GraphQL responses structured around widgets (varying by CE/EE/license)
- Filtering, bulk edit, list preferences hardcoded per widget in frontend
Upcoming work:
- Custom work item types GA (in progress)
- Field customization (post-GA): users can configure and sort fields per work item type
The tension:
- UX vision: users shouldn't distinguish between system-defined fields (assignees, labels) and custom fields (user-created string/int/select)
- Fields will be used as a term for "sidebar fields". Features will be functionality in the main view. Currently both are widgets under the hood.
- Current architecture: custom fields live inside a widget, separate from other fields
- Field customization requires: sortable fields with relative positioning across all fields
Problem Statement
Core issue: Widget architecture doesn't support the field customization vision.
Specific problems:
-
Conceptual hierarchy mismatch
- Custom fields are inside the custom fields widget
- Need to elevate custom fields to same level as other fields (assignees, labels, etc.)
- Can't save relative positioning between custom fields and system fields (two separate concepts)
-
Dual-purpose widgets
- Some widgets hold data (assignees, labels) - should be "fields"
- Some widgets provide functionality (Design Management, Development) - should be "features"
- Single concept (widget) conflates two different purposes
-
Hardcoded frontend logic (we can exclude/postpone this point if useful)
- Which widgets appear in filters: hardcoded
- Which widgets available for bulk edit: hardcoded
- Which widgets shown in list view preferences: hardcoded
- Adding new fields requires code changes across multiple surfaces -Hardcoded logic doesn't scale
-
API structure complexity
- Nested widget arrays require iteration to find data
- Complex inline fragments (
... on WorkItemWidgetDefinitionAssignees) - Harder to cache, harder for customers to consume
What happens if we do nothing:
- Can ship CWITs GA and field customization with current architecture
- Frontend needs to merge widgets + custom fields for display
- Need separate storage for field positioning
- Progressive messiness: more conditionals, dual code paths
- Velocity slows as complexity increases
- Impact on more advanced filtering and data-driven display logic
Proposed Concepts
Fields - data holders in the sidebar
- System-defined fields: assignees, labels, status, milestone (currently widgets)
- Custom fields: user-created string/int/select fields (currently inside custom fields widget)
- Treated equally in UX and code
- Configurable and sortable per work item type
Features - functionality in main content area
- Design Management, Development, Email Participants (currently widgets)
- Distinction: sidebar = field, main tab = feature
Goal: Replace widgets with fields + features as core concepts.
Options
Option 1: Do Nothing
Description:
- Keep widget architecture
- Custom fields stay inside custom fields widget
- Frontend merges widgets + custom fields for display where needed
- Store field positioning separately from widget/custom field data
- Continue hardcoding filtering/bulk edit/list preferences
Pros:
- No risk to GA timeline
- Proven architecture
- No migration complexity
Cons:
- Dual code paths (widgets vs. custom fields)
- Can't truly unify field positioning
- Hardcoded logic doesn't scale
- Frontend complexity increases with each new field
- Technical debt accumulates
Option 2: Backend Facade with Gradual Migration
Description:
- Backend adds facade/interface that exposes unified "fields" and "features" API
- Facade translates between fields
↔️ widgets/custom fields internally - Frontend consumes fields API from day one
- Backend incrementally migrates to native fields architecture over time
- Remove facade when underlying layer supports fields natively
- Facade handles both queries and mutations
Pros:
- Frontend builds on future-ready API immediately
- Backend can evolve incrementally
- Mutations handled in backend (positioning logic centralized)
- Clear migration path
Cons:
- Performance cost > 0 for facade translation layer
- Complexity during transition period
- Need to maintain both APIs in parallel
- If facade handles full CRUD, might as well build real fields API (see Option 4)
Open questions:
- Where does facade live? GraphQL layer? Service layer?
- What triggers facade removal? How do we know backend is "ready"?
- If we're building full fields CRUD in facade, why not just refactor backend? Data migration complexity.
When this makes sense:
- Want frontend to use fields API now
- Full backend refactor is multi-quarter effort
- Willing to accept transition complexity
- Have clear plan for facade removal
Option 3: Full Backend Refactor to Fields
Description:
- Rewrite backend to use fields + features as core concepts
- Widgets concept removed/deprecated
- Custom fields become instances of fields (no special widget)
- Flatten GraphQL response structure (workItem.assignees instead of workItem.widgets[].assignees)
- Breaking API change (manageable since API is beta/experimental)
Pros:
- Clean architecture aligned with vision
- No translation layer (best performance)
- Single code path for all fields
- Enables true data-driven frontend
- Easier for customers to consume API
- Better caching in Apollo (flattened response)
Cons:
- Significant backend work upfront
- Data migration complexity
- Iteration 3 will expand in scope
- Need to maintain both APIs during transition
When this makes sense:
- Want to avoid technical debt
- Long-term scalability is critical
But option 3 also aims to do option 4 work but not now (or directly after GA).
Key Questions to Answer
On approach:
- Do we need to decide now, or can we wait until scoping field customization?
- What's the forcing function? What breaks if we delay?
- Can we ship field customization with Option 1, just messier? Do we want that?
On API design (if not Option 1):
- Do we expose "fields" concept via API, or keep it internal?
- Do we flatten GraphQL response? (affects caching, REST API generation, customer consumption)
- How do we handle CE/EE/license differences? Nullify unavailable fields vs. omit them?
On backend architecture (if not Option 1):
- What's stored in database? Field definitions? Positioning?
- How do we handle "features" (Design Management, Development)? Stay as widgets?
- Do widgets become fields + features internally, or just expose that way?
On migration (if not Option 1):
- Can we run both APIs in parallel? For how long?
- What's deprecation timeline for widget-based API?
- Mostly internal usage so far - what's migration impact?
On scope:
- Is this one refactor or multiple? (fields concept, features concept, API flattening, dynamic configuration)
- What's minimum viable change to unblock field customization?
- Can we do this incrementally?
Risks
Risk: Postponing the decision
- Each feature built on widgets adds migration cost later
- Refactoring becomes "too expensive" and never happens
Risk: Premature refactor
- Build facade/refactor before fully understanding field customization requirements
- Might over-engineer for problems that won't occur
Risk: Partial migration
- Start Option 3 (facade) but never complete migration to Option 4
- Permanent translation layer with performance cost
- Mixed patterns across codebase (some use fields, some use widgets)
- Unclear which approach to follow for new features
Risk: Scalability ceiling
- Current architecture might work for 20 fields but not 100+
- Breaking point unclear until we hit it
- By then, migration is more expensive and disruptive
Recommendation
For CWITs GA: Option 1 (do nothing) - ship with current architecture, but consider end goal when making decisions. For example if we decide to flatten API response, let's keep fields and features in mind.
For fields/widget customization: Option 2 (facade) probably more doable than Option 3 (full refactor) given when we want to ship Iteration 3 (fields customization).
Key principle: Don't accumulate technical debt that assumes widgets are permanent. Each decision should move toward or at least not block the fields/features vision.
Next Steps
- Discuss this investigation - align on direction (not necessarily implementation timeline)
- Document decision
