Ban usage of `GitLab.com?` etc in codebase
## Problem
We have the ability to use `Gitlab.com?` (and friends such as `Gitlab.dev_env_org_or_com?`) as code.
Outside of narrow concerns, this should be discouraged in favor of data-driven approaches.
### Why is this a problem ?
1. Affects GitLab JH, and any other future SaaS instances
1. "Contagious" technical debt
1. Proliferation of conditional. When a related place uses `if GitLab.com?`, another piece of code, e.g. view will also need `if GitLab.com?`. \[1\]
1. Testing complexity - https://gitlab.com/groups/gitlab-org/-/epics/1969#note_226189771)
1. Makes this hard to test locally
1. Missed opportunity to provide similar features to Self-managed
1. Open source stewardship. Probably in conflict with our [open-source stewardship](https://about.gitlab.com/company/stewardship/) responsibilities
\[1\] See also contagion metric from https://technology.riotgames.com/news/taxonomy-tech-debt article
### Current Scenarios where we check `.com`/SaaS
1. Subscriptions/Billing specific items
- [JH doesn't want to apply Free User Cap](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89474)
- scenario type: `strategic`
- current solution: `prepend_mod` in Core product and then overriding in `jh`
- [General Billing items](https://gitlab.com/gitlab-org/gitlab/blob/df6065f3fe7155bd7f8ce3a976e326e8e40951b0/ee/lib/ee/sidebars/groups/menus/settings_menu.rb#L127-127)
- scenario type: `feature`(?)
- current solution: use `Gitlab::CurrentSettings.should_check_namespace_plan?` which is a combination of an application setting and checking this `Rails.env.test? || ::Gitlab.org_or_com?` based on `url` value and similar.
1. [Experimentation](https://gitlab.com/gitlab-org/gitlab/blob/df6065f3fe7155bd7f8ce3a976e326e8e40951b0/lib/gitlab/experiment/rollout/feature.rb#L16-16)
- scenario type: `strategic`
- current solution: use `Gitlab.com?` which validates the `url` or the environment variable `GITLAB_SIMULATE_SAAS` in `development`
1. Adding features only in `jh`
- [Add phone number to registration form](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86844)
- scenario type: `feature`
- current solution: add an application setting for this feature.
## Possible solutions to `jh` and our own code delivery/organization of `.com` code
1. Implement a `feature_available?` concept described in https://gitlab.com/groups/gitlab-org/-/epics/7374#note_990484289
- pros:
- scaleable
- provides context as to why something is guarding for a specific instance.
- makes it hard to disable unintentionally like is possible with an application setting
- cons: May inadvertently make targeting code for only one instance easier.
1. Use `prepend_mod` as seen in the [Free User Cap](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89474) above.
- pros: allows override
- cons:
- implementation is fragile for `jh` in the scenario we change the code in the core product and their prepended code may be broken if we change the item they are overriding.
- `jh` is always playing catchup to remove/analyze new features and then either accept or override them. Automatic `Opt-in`, which is likely unavoidable.
1. Use and enum instance and described above in https://gitlab.com/groups/gitlab-org/-/epics/7374#note_985157601
- pros:
- shifts current logic overall from `url` based with override on `jh` and others(?) of the `Gitlab.com?` method if they want `.com` features.
- makes the `.com` items `Opt-in` instead of `Opt-out`. Ideally in this case `jh` would add `instance_jh?` to items they want to accept as well from `.com` and we'd no longer be blind to `jh` as much in the core product. I'm not sure this is what we'd want, but at least we might not break them as easily as we could with the `prepend_mod` approach.
- cons:
- `jh` no longer gets features for purely `.com` by default if it is a new feature/area in `.com`. Could be a `pro` as well here as they wouldn't get `free user cap` by default as we would likely have this behind a `instance_com?` check.
- Cases where `jh` or other future instances want specific items but not others from `.com`, so they'd have to put their instance check(`instance_jh?`) in a lot of places in the core product and that may not be what we want. However, this fits the `Opt-in` concept of this setting.
1. Create a new application setting for each `.com`/instance specific feature - see [Add phone number to registration](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86844)
- pros:
- Allows each instance to branch its own way. (this could also be a `con`)
- Could be used as either `Opt-in` or `Opt-out` by default depending on the default setting. Likely assume `Opt-out` and default the setting `on`.
- May allow current development practices overall to remain unchanged(? Not sure about this one)
- cons:
- Does this scale? We'd be promoting adding many new application settings and perhaps adding another layer of coordination on GitLab side with `jh`. Sometimes they'd only exist if instances of `jh` didn't want them or wanted to branch in a different way.
<details><summary>previous proposal for more detail</summary>
Use application settings. This enables granular settings so that each SaaS instance can switch things according to their need.
For example, we have the `should_check_namespace_plan` application setting if the SaaS instance wants to check if the instance should check namespace-based plan checking (as opposed to instance-based plan checking). [ Note: It is not a simple matter of shifting from `if GitLab.com?` to `should_check_namespace_plan?`. The analogy is that we don't use a single feature flag for all features either ]
We can consider having [SaaS-only application settings](https://gitlab.com/gitlab-org/gitlab/-/issues/225101#note_790567065) as well
</details>
5. Separate `.com` code into a `/com` layer
- pros:
- Allows us to layer `/com` code like we do with `/ee` and get all the inherent benefits there.
- Allows easier(? opinion) understanding when code is for `SaaS` and not other instances.
- cons:
- With this, we'd assume instances like `jh` would or wouldn't get the `/com` layer
- If they did get the `/com` layer, it doesn't solve the `jh` issue of wanting to `Opt-out`/branch from `.com` code. They would still look to `Opt-out` at times and want to use `prepend_mod` or other strategies.
- If they didn't get the `/com` layer then they wouldn't get the billing or subscription code at times and that causes a whole other set of issues.
6. Do nothing
- pros: Nothing to do. But perhaps we should push more towards not having this issue in other ways? Encourage other instances to not `Opt-out` or something else?
- cons: Doesn't solve our issue and still promotes use of `.com?` in the codebase.
## Iterations
1. Convert an existing call to an application setting
1. Write Rubocop to ban `Gitlab.com?` (and friends) from being called.
### Detail
#### Numbers
The number of calls for `GitLab.com?` is growing steadily
| Version | Total |
|---------|-------|
| master | 196 |
| 13-12-stable-ee | 178 |
| 13-11-stable-ee | 158 |
| 13-10-stable-ee | 126 |
| 13-9-stable-ee | 119 |
| 13-8-stable-ee | 113 |
| 13-7-stable-ee | 112 |
| 13-6-stable-ee | 108 |
| 13-5-stable-ee | 101 |
| 13-4-stable-ee | 96 |
| 13-3-stable-ee | 88 |
| 13-2-stable-ee | 83 |
| 13-1-stable-ee | 80 |
| 13-0-stable-ee | 76 |
<details>
<summary>Detail for each method</summary>
| Version | Gitlab.canary? | Gitlab.com do | Gitlab.com? | Gitlab.com_and_canary? | Gitlab.com_but_not_canary? | Gitlab.dev_env_or_com? | Gitlab.dev_env_org_or_com? | Gitlab.dev_or_test_env? | Gitlab.extensions | Gitlab.gl_subdomain? | Gitlab.jh do | Gitlab.jh? | Gitlab.org? | Gitlab.staging? |
|---------|----------------|---------------|-------------|------------------------|----------------------------|------------------------|----------------------------|-------------------------|-------------------|----------------------|--------------|------------|-------------|-----------------|
| master | 1 | 1 | 130 | 1 | 2 | 26 | 5 | 16 | 1 | 0 | 2 | 4 | 1 | 6 |
| 13-12-stable-ee | 1 | 1 | 117 | 1 | 2 | 25 | 5 | 13 | 1 | 0 | 2 | 4 | 1 | 5 |
| 13-11-stable-ee | 1 | 1 | 110 | 1 | 2 | 19 | 5 | 7 | 0 | 0 | 2 | 4 | 1 | 5 |
| 13-10-stable-ee | 1 | 1 | 92 | 1 | 2 | 16 | 5 | 4 | 0 | 0 | 0 | 0 | 1 | 3 |
| 13-9-stable-ee | 1 | 1 | 88 | 2 | 2 | 13 | 5 | 3 | 0 | 0 | 0 | 0 | 0 | 4 |
| 13-8-stable-ee | 1 | 0 | 87 | 1 | 2 | 11 | 5 | 3 | 0 | 0 | 0 | 0 | 0 | 3 |
| 13-7-stable-ee | 1 | 0 | 86 | 1 | 2 | 11 | 5 | 4 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-6-stable-ee | 1 | 0 | 81 | 1 | 2 | 12 | 4 | 5 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-5-stable-ee | 1 | 0 | 74 | 1 | 2 | 13 | 4 | 4 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-4-stable-ee | 1 | 0 | 72 | 1 | 2 | 12 | 4 | 2 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-3-stable-ee | 1 | 0 | 65 | 1 | 2 | 11 | 4 | 2 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-2-stable-ee | 1 | 0 | 63 | 1 | 2 | 8 | 4 | 2 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-1-stable-ee | 1 | 0 | 62 | 1 | 2 | 6 | 4 | 2 | 0 | 0 | 0 | 0 | 0 | 2 |
| 13-0-stable-ee | 1 | 0 | 62 | 1 | 2 | 6 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
</details>
Generated with [count.rb](/uploads/7c1b26772db2e91b87a91be8fbf08b41/count.rb)
#### So what is acceptable ?
* Consider using application setting, like `should_check_namespace_plan?`
* Or, consider [plan limits](https://docs.gitlab.com/ee/development/application_limits.html#development)
* For ~"Enterprise Edition", also consider using License `feature_available?`
* `require_foo_feature?` - finer grained checks - https://gitlab.com/gitlab-org/gitlab/-/issues/225101#note_602424460
* For strictly SaaS only concerns, we can create a `saas_only_application_settings` table. This way concerns like CustomersDot can be effectively scoped to only the SaaS instance that requires it.
## See also
* https://gitlab.slack.com/archives/C02PF508L/p1592438620302400 (internal Slack thread)
* https://gitlab.com/groups/gitlab-org/-/epics/1969#note_225793532
* https://docs.gitlab.com/ee/development/application_limits.html#development
epic