diff --git a/doc/development/internal_analytics/internal_event_instrumentation/introduction.md b/doc/development/internal_analytics/internal_event_instrumentation/introduction.md
deleted file mode 100644
index 1becf2da6aec93c37a1b4b219cf9ac045e22e3c9..0000000000000000000000000000000000000000
--- a/doc/development/internal_analytics/internal_event_instrumentation/introduction.md
+++ /dev/null
@@ -1,13 +0,0 @@
----
-stage: Monitor
-group: Analytics Instrumentation
-info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review.
----
-
-# Internal event tracking
-
-This page is under construction. It serves as placeholder for the following information:
-
-- High level introduction
-- Difference between Events and Metrics
-- Basic overview of the architecture
diff --git a/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml b/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml
new file mode 100644
index 0000000000000000000000000000000000000000..71d7a9d1ea95a20acb25dfb7b6b202912950462c
--- /dev/null
+++ b/ee/elastic/docs/20240119130539_reindex_notes_to_update_analyzer.yml
@@ -0,0 +1,10 @@
+---
+name: ReindexNotesToUpdateAnalyzer
+version: '20240119130539'
+description: This migration reindexes the notes index to start using new analyzer for notes.note
+group: group::global search
+milestone: '16.9'
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/142301
+obsolete: false
+marked_obsolete_by_url:
+marked_obsolete_in_milestone:
diff --git a/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb b/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ddb293cf7d943f60c7e63523237ac28b02733585
--- /dev/null
+++ b/ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class ReindexNotesToUpdateAnalyzer < Elastic::Migration
+  def migrate
+    Elastic::ReindexingTask.create!(targets: %w[Note], options: { skip_pending_migrations_check: true })
+  end
+
+  def completed?
+    true
+  end
+end
diff --git a/ee/lib/elastic/latest/note_config.rb b/ee/lib/elastic/latest/note_config.rb
index fcb55b5f6152a2c5f21a11f3f55b817c9098d03b..4313ee1217bb33cdc0ae5e2e53a0aa412bdf7328 100644
--- a/ee/lib/elastic/latest/note_config.rb
+++ b/ee/lib/elastic/latest/note_config.rb
@@ -21,7 +21,7 @@ module NoteConfig
 
         indexes :id, type: :integer
 
-        indexes :note, type: :text, index_options: 'positions'
+        indexes :note, type: :text, index_options: 'positions', analyzer: :code_analyzer
         indexes :project_id, type: :integer
 
         indexes :noteable_type, type: :keyword
diff --git a/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb b/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b6e0ea081910ca03ace3aa05f0ae9563983278e7
--- /dev/null
+++ b/ee/spec/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_relative 'migration_shared_examples'
+require File.expand_path('ee/elastic/migrate/20240119130539_reindex_notes_to_update_analyzer.rb')
+
+RSpec.describe ReindexNotesToUpdateAnalyzer, feature_category: :global_search do
+  let(:version) { 20240119130539 }
+  let(:migration) { described_class.new(version) }
+
+  it 'does not have migration options set', :aggregate_failures do
+    expect(migration).not_to be_batched
+    expect(migration).not_to be_retry_on_failure
+  end
+
+  describe '#migrate' do
+    it 'creates reindexing task with correct target and options' do
+      expect { migration.migrate }.to change { Elastic::ReindexingTask.count }.by(1)
+      task = Elastic::ReindexingTask.last
+      expect(task.targets).to eq(%w[Note])
+      expect(task.options).to eq({ 'skip_pending_migrations_check' => true })
+    end
+  end
+
+  describe '#completed?' do
+    it 'always returns true' do
+      expect(migration.completed?).to eq(true)
+    end
+  end
+end
diff --git a/ee/spec/lib/gitlab/elastic/search_results_spec.rb b/ee/spec/lib/gitlab/elastic/search_results_spec.rb
index ae813aef486b129636e21eafa979654ded2cccef..0faa50f57542e0fb24d3cfeb2992d0e4aeff4142 100644
--- a/ee/spec/lib/gitlab/elastic/search_results_spec.rb
+++ b/ee/spec/lib/gitlab/elastic/search_results_spec.rb
@@ -14,53 +14,6 @@
   let(:project_2) { create(:project, :public, :repository, :wiki_repo) }
   let(:limit_project_ids) { [project_1.id] }
 
-  shared_context 'with elastic code examples' do
-    let(:code_examples) do
-      {
-        'perlMethodCall' => '$my_perl_object->perlMethodCall',
-        '"absolute_with_specials.txt"' => '/a/longer/file-path/absolute_with_specials.txt',
-        '"components-within-slashes"' => '/file-path/components-within-slashes/',
-        'bar\(x\)' => 'Foo.bar(x)',
-        'someSingleColonMethodCall' => 'LanguageWithSingleColon:someSingleColonMethodCall',
-        'javaLangStaticMethodCall' => 'MyJavaClass::javaLangStaticMethodCall',
-        'IllegalStateException' => 'java.lang.IllegalStateException',
-        'tokenAfterParentheses' => 'ParenthesesBetweenTokens)tokenAfterParentheses',
-        'ruby_call_method_123' => 'RubyClassInvoking.ruby_call_method_123(with_arg)',
-        'ruby_method_call' => 'RubyClassInvoking.ruby_method_call(with_arg)',
-        '#ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).',
-        'ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).',
-        'tokenAfterCommaWithNoSpace' => 'WouldHappenInManyLanguages,tokenAfterCommaWithNoSpace',
-        'missing_token_around_equals' => 'a.b.c=missing_token_around_equals',
-        'and;colons:too$' => 'and;colons:too$',
-        '"differeñt-lønguage.txt"' => 'another/file-path/differeñt-lønguage.txt',
-        '"relative-with-specials.txt"' => 'another/file-path/relative-with-specials.txt',
-        'ruby_method_123' => 'def self.ruby_method_123(ruby_another_method_arg)',
-        'ruby_method_name' => 'def self.ruby_method_name(ruby_method_arg)',
-        '"dots.also.need.testing"' => 'dots.also.need.testing',
-        '.testing' => 'dots.also.need.testing',
-        'dots' => 'dots.also.need.testing',
-        'also.need' => 'dots.also.need.testing',
-        'need' => 'dots.also.need.testing',
-        'tests-image' => 'extends: .gitlab-tests-image',
-        'gitlab-tests' => 'extends: .gitlab-tests-image',
-        'gitlab-tests-image' => 'extends: .gitlab-tests-image',
-        'foo/bar' => 'https://s3.amazonaws.com/foo/bar/baz.png',
-        'https://test.or.dev.com/repository' => 'https://test.or.dev.com/repository/maven-all',
-        'test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
-        'repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
-        'https://test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
-        'bar-baz-conventions' => 'id("foo.bar-baz-conventions")',
-        'baz-conventions' => 'id("foo.bar-baz-conventions")',
-        'baz' => 'id("foo.bar-baz-conventions")',
-        'bikes-3.4' => 'include "bikes-3.4"',
-        'sql_log_bin' => 'q = "SET @@session.sql_log_bin=0;"',
-        'sql_log_bin=0' => 'q = "SET @@session.sql_log_bin=0;"',
-        'v3/delData' => 'uri: "v3/delData"',
-        '"us-east-2"' => 'us-east-2'
-      }
-    end
-  end
-
   describe '#highlight_map' do
     using RSpec::Parameterized::TableSyntax
 
@@ -533,6 +486,26 @@
       expect(results.notes_count).to eq 2
     end
 
+    context 'when comment has some code snippet' do
+      before do
+        code_examples.values.uniq.each do |note|
+          sha = Digest::SHA256.hexdigest(note)
+          create(:note_on_issue, noteable: issue, project: project_1, commit_id: sha, note: note)
+        end
+        ensure_elasticsearch_index!
+      end
+
+      include_context 'with code examples' do
+        it 'finds all examples' do
+          code_examples.each do |query, description|
+            sha = Digest::SHA256.hexdigest(description)
+            notes = described_class.new(user, query, limit_project_ids).objects('notes')
+            expect(notes.map(&:commit_id)).to include(sha)
+          end
+        end
+      end
+    end
+
     it 'returns empty list when notes are not found' do
       results = described_class.new(user, 'security', limit_project_ids)
 
@@ -1096,7 +1069,7 @@ def search_for(term)
         ensure_elasticsearch_index!
       end
 
-      include_context 'with elastic code examples' do
+      include_context 'with code examples' do
         it 'finds all examples' do
           code_examples.each do |search_term, file_content|
             file_name = Digest::SHA256.hexdigest(file_content)
@@ -1308,7 +1281,7 @@ def search_for(term)
           )
         end
 
-        include_context 'with elastic code examples' do
+        include_context 'with code examples' do
           before do
             examples.values.uniq.each do |description|
               sha = Digest::SHA256.hexdigest(description)
diff --git a/ee/spec/requests/api/graphql/project/branch_rules_spec.rb b/ee/spec/requests/api/graphql/project/branch_rules_spec.rb
index 420038e35aab601d61c497c5ca577ae1dd60d32f..3e16ab22031c19b42284d2db075f2bdfbbd264d4 100644
--- a/ee/spec/requests/api/graphql/project/branch_rules_spec.rb
+++ b/ee/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -81,26 +81,35 @@ def expect_n_matching_branches_count_fields(count)
     describe 'response' do
       let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first }
       let_it_be(:branch_name_b) { 'diff-*' }
-      let_it_be(:branch_rule_a) do
+      let_it_be(:protected_branch_a) do
         create(:protected_branch, project: project, name: branch_name_a)
       end
 
-      let_it_be(:branch_rule_b) do
+      let_it_be(:protected_branch_b) do
         create(:protected_branch, project: project, name: branch_name_b)
       end
 
-      let_it_be(:external_status_check) do
+      let_it_be(:all_branches_external_status_check) do
         create(:external_status_check, project: project)
       end
 
-      let_it_be(:approval_project_rule) do
+      let_it_be(:all_branches_approval_rule) do
         create(:approval_project_rule, project: project)
       end
 
+      let_it_be(:all_protected_branches_approval_rule) do
+        create(:approval_project_rule, project: project, applies_to_all_protected_branches: true)
+      end
+
+      let(:branch_rule_a) { Projects::BranchRule.new(project, protected_branch_a) }
+      let(:branch_rule_b) { Projects::BranchRule.new(project, protected_branch_b) }
+      let(:all_branches_rule) { Projects::AllBranchesRule.new(project) }
+      let(:all_protected_branches_rule) { Projects::AllProtectedBranchesRule.new(project) }
       # branchRules are returned in alphabetical order
       let(:all_branches_rule_data) { branch_rules_data.first }
-      let(:branch_rule_b_data) { branch_rules_data.second }
-      let(:branch_rule_a_data) { branch_rules_data.third }
+      let(:all_protected_branches_rule_data) { branch_rules_data.second }
+      let(:branch_rule_b_data) { branch_rules_data.third }
+      let(:branch_rule_a_data) { branch_rules_data.fourth }
 
       before do
         post_graphql(query, current_user: current_user, variables: variables)
@@ -110,59 +119,74 @@ def expect_n_matching_branches_count_fields(count)
 
       it 'includes all fields', :use_sql_query_cache, :aggregate_failures do
         expect(all_branches_rule_data).to include(
-          'name' => 'All branches',
-          'isDefault' => false,
-          'isProtected' => false,
-          'matchingBranchesCount' => project.repository.branch_count,
-          'branchProtection' => nil,
-          'createdAt' => be_kind_of(String),
-          'updatedAt' => be_kind_of(String),
+          'name' => all_branches_rule.name,
+          'isDefault' => all_branches_rule.default_branch?,
+          'isProtected' => all_branches_rule.protected?,
+          'matchingBranchesCount' => all_branches_rule.matching_branches_count,
+          'branchProtection' => all_branches_rule.branch_protection,
+          'createdAt' => all_branches_rule.created_at.iso8601,
+          'updatedAt' => all_branches_rule.updated_at.iso8601,
           'approvalRules' => be_kind_of(Hash),
           'externalStatusChecks' => be_kind_of(Hash)
         )
         approval_rules_data = all_branches_rule_data['approvalRules']['nodes']
         expect(approval_rules_data).to eq([{
-          'id' => approval_project_rule.to_global_id.to_s,
-          'name' => approval_project_rule.name,
+          'id' => all_branches_approval_rule.to_global_id.to_s,
+          'name' => all_branches_approval_rule.name,
           'type' => 'REGULAR',
           'approvalsRequired' => 0
         }])
         external_checks_data = all_branches_rule_data['externalStatusChecks']['nodes']
         expect(external_checks_data).to eq([{
-          'id' => external_status_check.to_global_id.to_s,
-          'name' => external_status_check.name,
-          'externalUrl' => external_status_check.external_url
+          'id' => all_branches_external_status_check.to_global_id.to_s,
+          'name' => all_branches_external_status_check.name,
+          'externalUrl' => all_branches_external_status_check.external_url
+        }])
+        expect(all_protected_branches_rule_data).to include(
+          'name' => all_protected_branches_rule.name,
+          'isDefault' => all_protected_branches_rule.default_branch?,
+          'isProtected' => all_protected_branches_rule.protected?,
+          'matchingBranchesCount' => all_protected_branches_rule.matching_branches_count,
+          'branchProtection' => all_protected_branches_rule.branch_protection,
+          'createdAt' => all_protected_branches_rule.created_at.iso8601,
+          'updatedAt' => all_protected_branches_rule.updated_at.iso8601,
+          'approvalRules' => be_kind_of(Hash),
+          'externalStatusChecks' => be_kind_of(Hash)
+        )
+        approval_rules_data = all_protected_branches_rule_data['approvalRules']['nodes']
+        expect(approval_rules_data).to eq([{
+          'id' => all_protected_branches_approval_rule.to_global_id.to_s,
+          'name' => all_protected_branches_approval_rule.name,
+          'type' => 'REGULAR',
+          'approvalsRequired' => 0
         }])
 
         expect(branch_rule_a_data).to include(
-          'name' => branch_name_a,
-          'isDefault' => be_boolean,
-          'isProtected' => true,
-          'matchingBranchesCount' => 1,
+          'name' => branch_rule_a.name,
+          'isDefault' => branch_rule_a.default_branch?,
+          'isProtected' => branch_rule_a.protected?,
+          'matchingBranchesCount' => branch_rule_a.matching_branches_count,
           'branchProtection' => {
             "allowForcePush" => false,
             "codeOwnerApprovalRequired" => false
           },
-          'createdAt' => be_kind_of(String),
-          'updatedAt' => be_kind_of(String),
+          'createdAt' => branch_rule_a.created_at.iso8601,
+          'updatedAt' => branch_rule_a.updated_at.iso8601,
           'approvalRules' => be_kind_of(Hash),
           'externalStatusChecks' => be_kind_of(Hash)
         )
 
-        wildcard_count = TestEnv::BRANCH_SHA.keys.count do |branch_name|
-          branch_name.starts_with?('diff-')
-        end
         expect(branch_rule_b_data).to include(
-          'name' => branch_name_b,
-          'isDefault' => be_boolean,
-          'isProtected' => true,
-          'matchingBranchesCount' => wildcard_count,
+          'name' => branch_rule_b.name,
+          'isDefault' => branch_rule_b.default_branch?,
+          'isProtected' => branch_rule_b.protected?,
+          'matchingBranchesCount' => branch_rule_b.matching_branches_count,
           'branchProtection' => {
             "allowForcePush" => false,
             "codeOwnerApprovalRequired" => false
           },
-          'createdAt' => be_kind_of(String),
-          'updatedAt' => be_kind_of(String),
+          'createdAt' => branch_rule_a.created_at.iso8601,
+          'updatedAt' => branch_rule_a.updated_at.iso8601,
           'approvalRules' => be_kind_of(Hash),
           'externalStatusChecks' => be_kind_of(Hash)
         )
diff --git a/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb b/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb
new file mode 100644
index 0000000000000000000000000000000000000000..98b5e6ef6131357dcfc8052e6314a41b60d10be6
--- /dev/null
+++ b/ee/spec/support/shared_contexts/search/elastic/with_code_examples_shared_context.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+RSpec.shared_context 'with code examples' do
+  let(:code_examples) do
+    {
+      'perlMethodCall' => '$my_perl_object->perlMethodCall',
+      '"absolute_with_specials.txt"' => '/a/longer/file-path/absolute_with_specials.txt',
+      '"components-within-slashes"' => '/file-path/components-within-slashes/',
+      'bar\(x\)' => 'Foo.bar(x)',
+      'someSingleColonMethodCall' => 'LanguageWithSingleColon:someSingleColonMethodCall',
+      'javaLangStaticMethodCall' => 'MyJavaClass::javaLangStaticMethodCall',
+      'IllegalStateException' => 'java.lang.IllegalStateException',
+      'tokenAfterParentheses' => 'ParenthesesBetweenTokens)tokenAfterParentheses',
+      'ruby_call_method_123' => 'RubyClassInvoking.ruby_call_method_123(with_arg)',
+      'ruby_method_call' => 'RubyClassInvoking.ruby_method_call(with_arg)',
+      '#ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).',
+      'ambitious-planning' => 'We [plan ambitiously](#ambitious-planning).',
+      'tokenAfterCommaWithNoSpace' => 'WouldHappenInManyLanguages,tokenAfterCommaWithNoSpace',
+      'missing_token_around_equals' => 'a.b.c=missing_token_around_equals',
+      'and;colons:too$' => 'and;colons:too$',
+      '"differeñt-lønguage.txt"' => 'another/file-path/differeñt-lønguage.txt',
+      '"relative-with-specials.txt"' => 'another/file-path/relative-with-specials.txt',
+      'ruby_method_123' => 'def self.ruby_method_123(ruby_another_method_arg)',
+      'ruby_method_name' => 'def self.ruby_method_name(ruby_method_arg)',
+      '"dots.also.need.testing"' => 'dots.also.need.testing',
+      '.testing' => 'dots.also.need.testing',
+      'dots' => 'dots.also.need.testing',
+      'also.need' => 'dots.also.need.testing',
+      'need' => 'dots.also.need.testing',
+      'tests-image' => 'extends: .gitlab-tests-image',
+      'gitlab-tests' => 'extends: .gitlab-tests-image',
+      'gitlab-tests-image' => 'extends: .gitlab-tests-image',
+      'foo/bar' => 'https://s3.amazonaws.com/foo/bar/baz.png',
+      'https://test.or.dev.com/repository' => 'https://test.or.dev.com/repository/maven-all',
+      'test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
+      'repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
+      'https://test.or.dev.com/repository/maven-all' => 'https://test.or.dev.com/repository/maven-all',
+      'bar-baz-conventions' => 'id("foo.bar-baz-conventions")',
+      'baz-conventions' => 'id("foo.bar-baz-conventions")',
+      'baz' => 'id("foo.bar-baz-conventions")',
+      'bikes-3.4' => 'include "bikes-3.4"',
+      'sql_log_bin' => 'q = "SET @@session.sql_log_bin=0;"',
+      'sql_log_bin=0' => 'q = "SET @@session.sql_log_bin=0;"',
+      'v3/delData' => 'uri: "v3/delData"',
+      '"us-east-2"' => 'us-east-2'
+    }
+  end
+end
diff --git a/qa/Gemfile b/qa/Gemfile
index ec0537249067508131057b18b9efd760fa875229..7e6aa64258870941609c7bda406f884f8708b28b 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -2,7 +2,7 @@
 
 source 'https://rubygems.org'
 
-gem 'gitlab-qa', '~> 14', require: 'gitlab/qa'
+gem 'gitlab-qa', '~> 14', '>= 14.0.1', require: 'gitlab/qa'
 gem 'gitlab_quality-test_tooling', '~> 1.11.0', require: false
 gem 'gitlab-utils', path: '../gems/gitlab-utils'
 gem 'activesupport', '~> 7.0.8' # This should stay in sync with the root's Gemfile
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 26dae330ef6ad8ab40603eb8f8f40b9e92a9d28f..16306299bfe3a05ddfa8c1771755832f8547b41d 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -118,7 +118,7 @@ GEM
     gitlab (4.19.0)
       httparty (~> 0.20)
       terminal-table (>= 1.5.1)
-    gitlab-qa (14.0.0)
+    gitlab-qa (14.0.1)
       activesupport (>= 6.1, < 7.2)
       gitlab (~> 4.19)
       http (~> 5.0)
@@ -354,7 +354,7 @@ DEPENDENCIES
   faraday-retry (~> 2.2)
   fog-core (= 2.1.0)
   fog-google (~> 1.19)
-  gitlab-qa (~> 14)
+  gitlab-qa (~> 14, >= 14.0.1)
   gitlab-utils!
   gitlab_quality-test_tooling (~> 1.11.0)
   influxdb-client (~> 3.0)
diff --git a/spec/requests/api/graphql/project/branch_rules_spec.rb b/spec/requests/api/graphql/project/branch_rules_spec.rb
index 2ca37a491490bd890b3bd0a24f5e7f33d24c14c1..63537ba5750ce21f95db9d4f8d9f5fd1a921fd96 100644
--- a/spec/requests/api/graphql/project/branch_rules_spec.rb
+++ b/spec/requests/api/graphql/project/branch_rules_spec.rb
@@ -101,15 +101,17 @@ def expect_n_matching_branches_count_fields(count)
     describe 'response' do
       let_it_be(:branch_name_a) { TestEnv::BRANCH_SHA.each_key.first }
       let_it_be(:branch_name_b) { 'diff-*' }
-      let_it_be(:branch_rules) { [branch_rule_a, branch_rule_b] }
-      let_it_be(:branch_rule_a) do
+      let_it_be(:protected_branch_a) do
         create(:protected_branch, project: project, name: branch_name_a)
       end
 
-      let_it_be(:branch_rule_b) do
+      let_it_be(:protected_branch_b) do
         create(:protected_branch, project: project, name: branch_name_b)
       end
 
+      let(:branch_rule_a) { Projects::BranchRule.new(project, protected_branch_a) }
+      let(:branch_rule_b) { Projects::BranchRule.new(project, protected_branch_b) }
+      let(:branch_rules) { [branch_rule_a, branch_rule_b] }
       # branchRules are returned in alphabetical order
       let(:branch_rule_a_data) { branch_rules_data.dig(1, 'node') }
       let(:branch_rule_b_data) { branch_rules_data.dig(0, 'node') }