Feature Categorization

Introduced in GitLab 13.2.

Each Sidekiq worker, Batched Background migrations, controller action, test example or API endpoint must declare a feature_category attribute. This attribute maps each of these to a feature category. This is done for error budgeting, alert routing, and team attribution.

The list of feature categories can be found in the file config/feature_categories.yml. This file is generated from the stages.yml data file used in the GitLab Handbook and other GitLab resources.

Updating config/feature_categories.yml

Occasionally new features will be added to GitLab stages, groups, and product categories. When this occurs, you can automatically update config/feature_categories.yml by running scripts/update-feature-categories. This script will fetch and parse stages.yml and generate a new version of the file, which needs to be committed to the repository.

The Scalability team currently maintains the feature_categories.yml file. They will automatically be notified on Slack when the file becomes outdated.

Sidekiq workers

The declaration uses the feature_category class method, as shown below.

class SomeScheduledTaskWorker
  include ApplicationWorker

  # Declares that this worker is part of the
  # `continuous_integration` feature category
  feature_category :continuous_integration

  # ...

The feature categories specified using feature_category should be defined in config/feature_categories.yml. If not, the specs will fail.

Excluding Sidekiq workers from feature categorization

A few Sidekiq workers, that are used across all features, cannot be mapped to a single category. These should be declared as such using the feature_category :not_owned declaration, as shown below:

class SomeCrossCuttingConcernWorker
  include ApplicationWorker

  # Declares that this worker does not map to a feature category
  feature_category :not_owned # rubocop:disable Gitlab/AvoidFeatureCategoryNotOwned

  # ...

When possible, workers marked as “not owned” use their caller’s category (worker or HTTP endpoint) in metrics and logs. For instance, ReactiveCachingWorker can have multiple feature categories in metrics and logs.

Batched background migrations

Long-running migrations (as per the time limits guidelines) are pulled out as batched background migrations. They should define a feature_category, like this:

# File name: lib/gitlab/background_migration/my_background_migration_job.rb

class MyBackgroundMigrationJob < BatchedMigrationJob
  feature_category :gitaly

RuboCop::Cop::BackgroundMigration::FeatureCategory cop ensures a valid feature_category is defined.

Rails controllers

Specifying feature categories on controller actions can be done using the feature_category class method.

A feature category can be specified on an entire controller using:

class Boards::ListsController < ApplicationController
  feature_category :kanban_boards

The feature category can be limited to a list of actions using the second argument:

class DashboardController < ApplicationController
  feature_category :team_planning, [:issues, :issues_calendar]
  feature_category :code_review_workflow, [:merge_requests]

These forms cannot be mixed: if a controller has more than one category, every single action must be listed.

Excluding controller actions from feature categorization

In the rare case an action cannot be tied to a feature category this can be done using the not_owned feature category.

class Admin::LogsController < ApplicationController
  feature_category :not_owned

Ensuring feature categories are valid

The spec/controllers/every_controller_spec.rb will iterate over all defined routes, and check the controller to see if a category is assigned to all actions.

The spec also validates if the used feature categories are known. And if the actions used in configuration still exist as routes.

API endpoints

The GraphQL API is currently categorized as not_owned. For now, no extra specification is needed. For more information, see gitlab-com/gl-infra/scalability#583.

Grape API endpoints can use the feature_category class method, like Rails controllers do:

module API
  class Issues < ::API::Base
    feature_category :team_planning

The second argument can be used to specify feature categories for specific routes:

module API
  class Users < ::API::Base
    feature_category :user_profile, ['/users/:id/custom_attributes', '/users/:id/custom_attributes/:key']

Or the feature category can be specified in the action itself:

module API
  class Users < ::API::Base
    get ':id', feature_category: :user_profile do

As with Rails controllers, an API class must specify the category for every single action unless the same category is used for every action within that class.

RSpec Examples

You must set feature category metadata for each RSpec example. This information is used for flaky test issues to identify the group that owns the feature.

The feature_category should be a value from config/feature_categories.yml.

The feature_category metadata can be set:

Consider splitting the file in the case there are multiple feature categories identified in the same file.


 RSpec.describe Admin::Geo::SettingsController, :geo, feature_category: :geo_replication do

For examples that don’t have a feature_category set we add a warning when running them in local environment.

To disable the warning use RSPEC_WARN_MISSING_FEATURE_CATEGORY=false when running RSpec tests:

RSPEC_WARN_MISSING_FEATURE_CATEGORY=false bin/rspec spec/<test_file>

Additionally, we flag the offenses via RSpec/MissingFeatureCategory RuboCop rule.

Tooling feature category

For Engineering Productivity internal tooling we use feature_category: :tooling.

For example in spec/tooling/danger/specs_spec.rb.

Shared feature category

For features that support developers and they are not specific to a product group we use feature_category: :shared For example spec/lib/gitlab/job_waiter_spec.rb