diff --git a/doc/topics/awesome_co.md b/doc/topics/awesome_co.md
index e89dd6772780c31055abe6b8dac5542b1295329d..f6cad7eb804d76b05d3b17c7b02826807f7cb00e 100644
--- a/doc/topics/awesome_co.md
+++ b/doc/topics/awesome_co.md
@@ -141,6 +141,25 @@ create(:project, name: 'No longer throws error', owner: @owner, namespace: creat
 create(:epic, group: create(:group), author: @owner)
 ```
 
+#### `parsing id "my id" as "my_id"`
+
+See [specifying variables](#specify-a-variable)
+
+#### `id is invalid`
+
+Given that non-Ruby parsers parse IDs as Ruby Objects, the [naming conventions](https://docs.ruby-lang.org/en/2.0.0/syntax/methods_rdoc.html#label-Method+Names) of Ruby must be followed when specifying an ID.
+
+Examples of invalid IDs:
+
+- IDs that start with a number
+- IDs that have special characters (-, !, $, @, `, =, <, >, ;, :)
+
+#### ActiveRecord::AssociationTypeMismatch: Model expected, got ... which is an instance of String
+
+This is currently a limitation for the seeder.
+
+See the issue for [allowing parsing of raw Ruby objects](https://gitlab.com/gitlab-org/gitlab/-/issues/403079).
+
 ## YAML Factories
 
 ### Generator to generate _n_ amount of records
@@ -210,3 +229,50 @@ epics:
     start_date: <%= 1.day.ago %>
     due_date: <%= 1.month.from_now %>
 ```
+
+## Variables
+
+Each created factory can be assigned an identifier to be used in future seeding.
+
+You can specify an ID for any created factory that you may use later in the seed file.
+
+### Specify a variable
+
+You may pass an `_id` attribute on any factory to refer back to it later in non-Ruby parsers.
+
+Variables are under the factory definitions that they reside in.
+
+```yaml
+---
+group_labels:
+  - _id: my_label #=> group_labels.my_label
+
+projects:
+  - _id: my_project #=> projects.my_project
+```
+
+Variables:
+
+NOTE:
+It is not advised, but you may specify variables with spaces. These variables may be referred back to with underscores.
+
+### Referencing a variable
+
+Given a YAML seed file:
+
+```yaml
+---
+group_labels:
+  - _id: my_group_label #=> group_labels.my_group_label
+    name: My Group Label
+    color: "#FF0000"
+  - _id: my_other_group_label #=> group_labels.my_other_group_label
+    color: <%= group_labels.my_group_label.color %>
+
+projects:
+  - _id: my_project #=> projects.my_project
+    name: My Project
+```
+
+When referring to a variable, the variable refers to the _already seeded_ models. In other words, the model's `id` attribute will
+be populated.
diff --git a/ee/db/seeds/awesome_co/awesome_co.rb b/ee/db/seeds/awesome_co/awesome_co.rb
index 6f4f9dd738511b15fdd4ce380c44da6da8834fbb..26768ada0adf128f017265694d1b70c23ba5fe9b 100644
--- a/ee/db/seeds/awesome_co/awesome_co.rb
+++ b/ee/db/seeds/awesome_co/awesome_co.rb
@@ -1,5 +1,7 @@
 # frozen_string_literal: true
 
+require 'ostruct'
+
 module AwesomeCo
   class << self
     # Seed test data using AwesomeCo generator
@@ -45,23 +47,61 @@ class FactoryDefinition
       # @param [Array<String>] traits FactoryBot traits that should be applied
       # @param [Hash<String, String>] attributes Attributes to apply
       def initialize(factory, *traits, **attributes)
+        # "my id" #=> "my_id"
+        # "my_id" #=> "my_id"
         @id = attributes.delete('_id')
 
+        if @id
+          raise "id `#{@id}` is invalid" if @id.match?(/[\x21-\x2F\x3A-\x40\x5B-\x5E\x60\x7B-\x7F]/) # special chars
+          raise "id `#{@id}` is invalid. id cannot start with a number" if @id.match?(/^[0-9]/)
+
+          if @id.include?(' ')
+            new_id = @id.tr(' ', '_')
+            warn %(parsing id "#{@id}" as "#{new_id}")
+
+            @id = new_id
+          end
+        end
+
         @factory = factory
         @traits = traits
         @attributes = attributes
       end
 
+      # Build and save the Factory
+      # @param [Binding] parser_binding
+      # @return [ApplicationRecord] the built and saved Factory
       def fabricate(parser_binding)
-        Gitlab::AppLogger.info("Creating `#{@factory}` with traits `#{@traits}` and attributes `#{@attributes}`")
-
-        build(parser_binding).save
+        factory = if @id
+                    parser_binding.local_variable_get(@factory.pluralize)[@id]
+                  else
+                    build(parser_binding)
+                  end
+
+        factory.tap do |f|
+          f.save
+          Gitlab::AppLogger.info(
+            "Created `#{@factory}` with traits `#{@traits}` and attributes `#{@attributes}` [ID: #{f.id}]"
+          )
+
+          parser_binding.local_variable_get(@factory.pluralize)[@id] = f if @id
+        end
       end
 
+      # Build the Factory
+      # @param [Binding] parser_binding
+      # @return [ApplicationRecord] the built factory
       def build(parser_binding)
         @attributes.transform_values! { |v| v.is_a?(String) ? ERB.new(v).result(parser_binding) : v }
 
-        FactoryBot.build(@factory, *@traits, **@attributes)
+        FactoryBot.build(@factory, *@traits, **@attributes).tap do |factory|
+          next unless @id
+          next unless parser_binding.local_variable_defined?(@factory.pluralize)
+
+          raise "id `#{@id}` must be unique" if parser_binding.local_variable_get(@factory.pluralize)[@id]
+
+          parser_binding.local_variable_get(@factory.pluralize)[@id] = factory if @id
+        end
       end
     end
   end
@@ -89,13 +129,16 @@ def initialize(seed_file, owner)
       end
 
       def parse
+        raise 'Seed file must specify a name' unless @name
+
         # create the seeded group with a path that is hyphenated and random
         @group = FactoryBot.create(:group, name: @name,
                                                path: "#{@name.parameterize}-#{@owner.username}-#{SecureRandom.hex(3)}")
         @group.add_owner(@owner)
 
         @definitions.each do |factory, definitions|
-          @parser_binding.local_variable_set(factory, definitions)
+          # Using OpenStruct for dot-notation and saves a custom class impl. Ruby's discouragement does not apply
+          @parser_binding.local_variable_set(factory, OpenStruct.new) unless @parser_binding.local_variable_defined?(factory) # rubocop:disable Style/OpenStructUse
 
           @factories << FactoryDefinitions.new(factory, group, definitions)
         end
@@ -145,7 +188,7 @@ def parse
         begin
           @definitions = YAML.safe_load_file(@seed_file, aliases: true)
         rescue Psych::SyntaxError => e
-          # put the yaml seed file on the top of the backtrace to help with tracability
+          # put the yaml seed file on the top of the backtrace to help with traceability
           e.backtrace.unshift("#{@seed_file.path}:#{e.line}:#{e.column}")
           raise e, "Seed file is malformed. #{e.message}"
         end
diff --git a/ee/db/seeds/awesome_co/awesome_co.yml.erb b/ee/db/seeds/awesome_co/awesome_co.yml.erb
index 2fa71a662757e75592766ce70eb45217becfb79f..d1402c7b36d42362535b2a7b36d4237e8101a7fe 100644
--- a/ee/db/seeds/awesome_co/awesome_co.yml.erb
+++ b/ee/db/seeds/awesome_co/awesome_co.yml.erb
@@ -14,8 +14,7 @@ name: AwesomeCo
 
 group_labels:
   # Priorities
-  - _id: priority_1
-    name: priority::1
+  - name: priority::1
     group_id: <%= @group.id %>
     color: "#FF0000"
   - name: priority::2
@@ -29,24 +28,25 @@ group_labels:
     color: "#BB0000"
 
   # Squads
-  - name: squad::a
+  - _id: squad_a
+    name: squad::a
     group_id: <%= @group.id %>
     color: "#CCCCCC"
   - name: squad::b
     group_id: <%= @group.id %>
-    color: "#CCCCCC"
+    color: <%= group_labels.squad_a.color %>
   - name: squad::c
     group_id: <%= @group.id %>
-    color: "#CCCCCC"
+    color: <%= group_labels.squad_a.color %>
   - name: squad::d
     group_id: <%= @group.id %>
-    color: "#CCCCCC"
+    color: <%= group_labels.squad_a.color %>
   - name: squad::e
     group_id: <%= @group.id %>
-    color: "#CCCCCC"
+    color: <%= group_labels.squad_a.color %>
   - name: squad::f
     group_id: <%= @group.id %>
-    color: "#CCCCCC"
+    color: <%= group_labels.squad_a.color %>
 
   # Types
   - name: type::0-idea
diff --git a/ee/spec/db/seeds/awesome_co/awesome_co_spec.rb b/ee/spec/db/seeds/awesome_co/awesome_co_spec.rb
index 69b8f9619a91c1b71ac4983f0b57ee6ad885e546..c27ad6310c8dd59cc3e1adcd075ce921eef095c7 100644
--- a/ee/spec/db/seeds/awesome_co/awesome_co_spec.rb
+++ b/ee/spec/db/seeds/awesome_co/awesome_co_spec.rb
@@ -256,6 +256,38 @@ def initialize(_seed_file, owner)
               end
             end
           end
+
+          context 'when an id already exists' do
+            let(:seed_file_content) do
+              <<~YAML
+                name: Test
+                group_labels:
+                  - _id: my_label
+                    title: My Label
+                  - _id: my_label
+                    title: My other label
+              YAML
+            end
+
+            it 'raises a validation error' do
+              expect { parser.parse }.to raise_error(/id `my_label` must be unique/)
+            end
+          end
+        end
+
+        describe '#parse' do
+          context 'when name is not specified' do
+            let(:seed_file_content) do
+              <<~YAML
+                group_labels:
+                  - title: My Label
+              YAML
+            end
+
+            it 'raises an error when name is not specified' do
+              expect { parser.parse }.to raise_error(/Seed file must specify a name/)
+            end
+          end
         end
 
         context 'when parsed' do
@@ -302,6 +334,107 @@ def initialize(_seed_file, owner)
               parser.parse
             end
           end
+
+          describe '@parser_binding' do
+            let(:group_labels) { parser.instance_variable_get(:@parser_binding).local_variable_get('group_labels') }
+
+            context 'when a definition has an id' do
+              let(:seed_file_content) do
+                <<~YAML
+                  name: Test
+                  group_labels:
+                    - _id: my_label
+                      title: My Label
+                YAML
+              end
+
+              context 'when the id has spaces' do
+                let(:seed_file_content) do
+                  <<~YAML
+                    name: Test
+                    group_labels:
+                      - _id: id with spaces
+                        title: With Spaces
+                  YAML
+                end
+
+                it 'binds to an underscored variable', :aggregate_failures do
+                  parser.parse
+
+                  expect(group_labels).to respond_to(:id_with_spaces)
+                  expect(group_labels.id_with_spaces.title).to eq('With Spaces')
+                end
+
+                it 'renders a warning' do
+                  expect { parser.parse }.to output(%(parsing id "id with spaces" as "id_with_spaces"\n)).to_stderr
+                end
+              end
+
+              it 'binds the object', :aggregate_failures do
+                parser.parse
+
+                expect(group_labels).to be_a(OpenStruct) # rubocop:disable Style/OpenStructUse
+                expect(group_labels.my_label).to be_a(GroupLabel)
+                expect(group_labels.my_label.title).to eq('My Label')
+              end
+
+              context 'when id is malformed' do
+                shared_examples 'invalid id' do |message|
+                  it 'raises an error' do
+                    expect { parser.parse }.to raise_error(message)
+                  end
+                end
+
+                context 'when id contains invalid characters' do
+                  it_behaves_like 'invalid id', /id `--invalid-id` is invalid/ do
+                    let(:seed_file_content) do
+                      <<~YAML
+                        name: Test
+                        group_labels:
+                          - _id: --invalid-id
+                      YAML
+                    end
+                  end
+
+                  it_behaves_like 'invalid id', /id `invalid!id` is invalid/ do
+                    let(:seed_file_content) do
+                      <<~YAML
+                        name: Test
+                        group_labels:
+                          - _id: invalid!id
+                      YAML
+                    end
+                  end
+
+                  it_behaves_like 'invalid id', /id `1_label` is invalid. id cannot start with a number/ do
+                    let(:seed_file_content) do
+                      <<~YAML
+                        name: Test
+                        group_labels:
+                          - _id: 1_label
+                      YAML
+                    end
+                  end
+                end
+              end
+            end
+
+            context 'when a definition does not have an id' do
+              let(:seed_file_content) do
+                <<~YAML
+                  name: Test
+                  group_labels:
+                    - title: Test
+                YAML
+              end
+
+              it 'does not bind the object' do
+                parser.parse
+
+                expect(group_labels.to_h).to be_empty
+              end
+            end
+          end
         end
       end
     end
diff --git a/lib/tasks/gitlab/feature_categories.rake b/lib/tasks/gitlab/feature_categories.rake
index cecfaf3cb363eceb541134ee761fbd84344f6637..db49601215879ae3de2b2e0b59037fb3c4d68fdf 100644
--- a/lib/tasks/gitlab/feature_categories.rake
+++ b/lib/tasks/gitlab/feature_categories.rake
@@ -15,7 +15,7 @@ namespace :gitlab do
         hash[feature_category] << {
           klass: controller.to_s,
           action: action,
-          source_location: source_location(controller, action)
+          source_location: src_location(controller, action)
         }
       end
 
@@ -28,7 +28,7 @@ namespace :gitlab do
         hash[feature_category] << {
           klass: klass.to_s,
           action: path,
-          source_location: source_location(klass)
+          source_location: src_location(klass)
         }
       end
 
@@ -40,7 +40,7 @@ namespace :gitlab do
         hash[feature_category] ||= []
         hash[feature_category] << {
           klass: worker.klass.name,
-          source_location: source_location(worker.klass.name)
+          source_location: src_location(worker.klass.name)
         }
       end
 
@@ -60,7 +60,14 @@ namespace :gitlab do
                      'database_tables' => database_tables)
     end
 
-    def source_location(klass, method = nil)
+    private
+
+    # Source location of the trace
+    # @param [Class] klass
+    # @param [Method,UnboundMethod] method
+    # @note This method was named `source_location` but this name shadowed Binding#source_location
+    # @note This method was made private as it is not being used elsewhere
+    def src_location(klass, method = nil)
       file, line =
         if method && klass.method_defined?(method)
           klass.instance_method(method).source_location