Modernize CustomersDot trials architecture for GitLab.com scalability and maintainability
## Problem
The `trials_controller.rb` on CustomersDot has grown into a large, difficult-to-maintain file that handles multiple trial types and flows:
- Does not follow REST conventions (contains methods like `def create_addon`, `def create_iterable`, etc.)
- Mixes concerns for different trial types (Ultimate, Duo Pro, Duo Enterprise, add-ons, etc.)
- Handles both GitLab.com (namespace-based subscriptions) and self-managed trials in the same controller
This was identified in [MR review comment](https://gitlab.com/gitlab-org/customers-gitlab-com/-/merge_requests/10425#note_2010275251).
## Proposal
Refactor the trials controller into separate, focused controllers organized by trial type and subscription model.
### Scope
**This refactoring targets GitLab.com (namespace-based subscriptions) only.** Self-managed trial flows will remain in the existing controller structure to avoid breaking older self-managed instances that cannot be easily updated.
### Proposed Structure
Split GitLab.com trial endpoints into namespaced controllers:
```
trials/gitlab_com/
├── ultimates_controller.rb # Creates trials
└── addons_controller.rb # Creates trials
leads/gitlab_com/
├── ultimates_controller.rb # Creates trial-related leads
├── addons_controller.rb # Creates addon-related leads
├── iterables_controller.rb # Creates iterable leads
└── hand_raises_controller.rb # Creates
```
**GitLab.com (Namespace-based subscriptions):**
Trials:
- `trials/gitlab_com/ultimates_controller.rb` - Ultimate trial creation for namespaces
- `trials/gitlab_com/addons_controller.rb` - Add-on trials (Duo Pro, Duo Enterprise, etc.)
Leads:
- `leads/gitlab_com/iterable_controller.rb` - Iterable lead creation
- `leads/gitlab_com/hand_raise_controller.rb` - Hand raise lead creation
- `leads/gitlab_com/ultimates_controller.rb` - Trial-related leads only
- `leads/gitlab_com/addons_controller.rb` - Add-on trials (Duo Pro, Duo Enterprise, etc.)
**Self-managed (unchanged):**
- Keep existing self-managed trial logic in current `trials_controller.rb`
- Cannot easily migrate to `trials/self_managed/*` namespace due to backward compatibility requirements with older self-managed instances
- Use route redirects if needed to maintain compatibility
**Shared concerns:**
- Extract common behavior from `before_actions` into reusable concerns
- DRY up authentication, authorization, and validation logic
### Controller Naming Convention
All controllers will follow Rails conventions and use **plural names**, even when the plural form may feel awkward (e.g., `ultimates_controller.rb`, `iterables_controller.rb`).
**Rationale:**
- **Convention over configuration**: Plural controller names are the Rails standard and require no custom routing configuration
- **Principle of least surprise**: Rails developers expect plural controllers; deviating creates cognitive overhead
- **Maintainability**: Custom route-to-controller mappings add complexity and potential confusion for future contributors
- **Consistency**: Aligns with existing Rails patterns across the codebase
**Trade-off considered:**
While singular names (e.g., `ultimate_controller.rb`) would read more naturally, they would require explicit routing configuration:
```ruby
resources :ultimates, controller: 'ultimate' # Custom mapping needed
```
This breaks Rails conventions and adds maintenance burden that outweighs the marginal readability improvement. We prioritize convention adherence over linguistic naturalness.
### Implementation Approach
1. Create new namespaced controllers for GitLab.com trials under `trials/gitlab_com/` and leads under `leads/gitlab_com/`
2. Migrate GitLab.com-specific trial creation logic to new controllers
3. Extract shared logic into concerns
4. Update routes to point to new controllers
5. Add route redirects from old GitLab.com endpoints to new ones (GitLab.com can be updated easily)
6. Leave self-managed trial logic in existing controller (backward compatibility for older instances)
### Rollout Strategy
**Phase 1: Build new endpoints (CustomersDot)**
1. Create new `trials/gitlab_com/*` and `leads/gitlab_com/*` controllers on CustomersDot
2. Implement new endpoints alongside existing ones (no breaking changes)
3. Deploy to production (new endpoints available but unused)
**Phase 2: Gradual migration (GitLab.com)**
1. Add feature flags on GitLab.com to control routing to new CustomersDot endpoints
- Example: `use_new_ultimate_trial_endpoint`, `use_new_addon_trial_endpoint`, `use_new_trial_leads_endpoint`, `use_new_iterable_lead_endpoint`, `use_new_hand_raise_lead_endpoint`
2. Roll out feature flags gradually
3. Monitor for errors and performance issues
**Phase 3: Cleanup (CustomersDot)**
1. Once all GitLab.com feature flags are at 100% and stable
2. Remove references to legacy paths in Gitlab.com code.
3. Deprecate legacy GitLab.com-specific code paths in old `trials_controller.rb`
4. Keep self-managed trial logic intact in `trials_controller.rb`
This approach allows safe, incremental migration with easy rollback if issues arise.
### Why Not `trials/self_managed/`?
While a `trials/self_managed/` namespace would be ideal for symmetry, self-managed instances running older versions would continue calling the original endpoints. Unlike GitLab.com (which we control and can update), we cannot force self-managed instances to upgrade, making route changes risky. Route redirects could handle this, but that adds complexity better suited for a separate refactoring effort focused specifically on self-managed trials.
## Result
- Clear separation between GitLab.com and self-managed trial flows
- Logical separation between trials (`trials/gitlab_com/`) and general leads (`leads/gitlab_com/`)
- Trial-related leads grouped with trials under `trials/gitlab_com/leads_controller.rb`
- Smaller, focused controllers following REST conventions for GitLab.com trials
- Easier maintenance and testing for GitLab.com flows
- No breaking changes for self-managed instances
- Foundation for future self-managed refactoring (if needed)
## Next Steps
1. Create implementation issues for each new GitLab.com controller
2. Define migration strategy and route redirects for existing GitLab.com trial endpoints
3. Plan feature flag rollout strategy for GitLab.com
4. Plan deprecation timeline for old GitLab.com endpoints on CustomersDot
## How Will We Measure Success?
- All GitLab.com trial endpoints migrated to new `trials/gitlab_com/*` structure
- All GitLab.com general lead endpoints migrated to new `leads/gitlab_com/*` structure
- Feature flags successfully rolled out to 100% on GitLab.com
- Legacy GitLab.com code paths removed from CustomersDot
- No regressions in trial creation flows (GitLab.com or self-managed)
- Reduced controller file sizes for new controllers (target: <200 lines per controller)
- Improved test coverage and maintainability for GitLab.com trial flows
epic