.ai/principles/distilled/backend-ee.md
Prerequisite: If you haven't already, also read .ai/principles/distilled/backend-ruby.md - it contains foundational rules that apply to all backend work.
ee/ top-level directory; keep CE files as close to unmodified as possible.ee/ without an EE namespace (e.g., ee/app/models/awesome.rb with class Awesome).ee/ with the EE namespace (e.g., ee/app/models/ee/user.rb with module EE::User).ee/spec/ without a second ee/ subdirectory (e.g., ee/spec/models/vulnerability_spec.rb).ee/spec/ including the second ee/ subdirectory (e.g., ee/spec/models/ee/user_spec.rb).ee/app/graphql/ee/{mutations,resolvers,types}.lib/) with no implementation, and extend them in ee/lib/ee/.ee/app/assets/javascripts/; use the ee_else_ce import alias for files that differ between CE and EE.ee/config/initializers; use Gitlab.ee { ... } in config/initializers only when splitting is not possible.Gitlab.ee { draw :ee_only }; use draw_all only when both CE and EE route files exist.ee/ directory structure.prepend_mod, extend_mod, or include_mod (not prepend, extend, or include) to inject EE modules into CE classes.prepend_mod_with, extend_mod_with, or include_mod_with when the EE module does not follow the default naming convention.prepend_mod (or variants) on the last line of the CE file where the class resides.extend ::Gitlab::Utils::Override and the override guard in every EE module that overrides a CE method.module EE to avoid naming conflicts.ActiveSupport::Concern with extend ::Gitlab::Utils::Override inside class_methods when overriding CE class methods.project.licensed_feature_available?(:my_feature_name).group.licensed_feature_available?(:my_feature_name).License.feature_available?(:my_feature_name).PREMIUM_FEATURES or ULTIMATE_FEATURES in ee/app/models/gitlab_subscriptions/features.rb based on the required plan.GLOBAL_FEATURES in ee/app/models/gitlab_subscriptions/features.rb.Gitlab::Saas.feature_available?(:feature_name); DO NOT use Gitlab.com? for new SaaS-only features.Gitlab::Dedicated.feature_available?(:feature_name); DO NOT use Gitlab::CurrentSettings.gitlab_dedicated_instance? directly in application code (migrations are an exception).Gitlab::Saas.feature_available? in CE code.push_licensed_feature in EE controllers to expose licensed features to the frontend via gon.licensed_features.FEATURES in ee/lib/gitlab/saas.rb and create a YAML definition in ee/config/saas_features/ using bin/saas-feature.rb.FEATURES in ee/lib/gitlab/dedicated.rb and create a YAML definition in ee/config/dedicated_features/.name field and has an owner (group).stub_licensed_features(my_feature: true) to enable licensed features in specs; DO NOT rely on implicit license state.:saas_my_feature_name) on describe/context/it blocks instead of manually calling stub_saas_features in before blocks where possible.stub_saas_features(feature: true/false) directly only for complex scenarios requiring granular control over enabled/disabled states within the same test.:saas metadata helper only for code that still relies on Gitlab.com? (e.g., database migrations); DO NOT use it for new SaaS-only features.ee/spec/.FactoryBot.modify to extend CE factories in EE; DO NOT define new factories inside a FactoryBot.modify block.RSpec.describe for EE extension specs, reference the CE class name (e.g., RSpec.describe User), not the EE module.ee/spec/frontend/ mirroring the CE directory structure.ee_else_ce/... in frontend specs when the component under test imports them with ee_else_ce/....toMatchObject instead of toEqual in CE frontend spec expect blocks when comparing objects that may have additional EE-only fields.render_if_exists instead of render in CE views to include EE-only partials.render_if_exists partial paths are relative to app/views/ or ee/app/views/ (not relative to the calling view's directory).render_ce inside EE partials to explicitly render the CE version of a partial and avoid infinite recursion.ee_component import alias to import EE components into CE components; DO NOT import directly from ee/ paths in CE code.glFeatures.myFeatureName (from gon.licensed_features) in EE Vue components to guard rendering behind a license check.ee_else_ce_jest alias when importing mock data in frontend specs that must work in both CE and EE environments.before_action action lists and params.permit attribute lists into separate methods so EE can extend them via super without merge conflicts.params :optional_project_params_ee do; end) in CE Grape helpers so EE can override them cleanly.enum key/value pairs in FOSS (CE) code; DO NOT define enums exclusively in EE.// EE-specific start / // EE-specific end comments; DO NOT nest EE-specific rules inside CE selectors using .ee- class qualifiers on the parent.~"pipeline:run-as-if-foss" label to MRs that contain features differing between FOSS and EE to run pipelines in both contexts.For the full picture, see: