diff --git a/config/feature_flags/gitlab_com_derisk/ci_fix_input_types.yml b/config/feature_flags/gitlab_com_derisk/ci_fix_input_types.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b910822c618d14ebce444dd8bf5e5f6815069874
--- /dev/null
+++ b/config/feature_flags/gitlab_com_derisk/ci_fix_input_types.yml
@@ -0,0 +1,9 @@
+---
+name: ci_fix_input_types
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/434826
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145257
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/443301
+milestone: '16.10'
+group: group::pipeline authoring
+type: gitlab_com_derisk
+default_enabled: false
diff --git a/lib/gitlab/ci/config/interpolation/block.rb b/lib/gitlab/ci/config/interpolation/block.rb
index dc1ca423a81673c821532b5b6b42966551384174..0c9a83fed1d575e481f7237e50f6db6d50b7a5c0 100644
--- a/lib/gitlab/ci/config/interpolation/block.rb
+++ b/lib/gitlab/ci/config/interpolation/block.rb
@@ -17,7 +17,15 @@ class Block
           PATTERN = /(?<block>\$\[\[\s*(?<data>\S{1}.*?\S{1})\s*\]\])/
           MAX_FUNCTIONS = 3
 
-          attr_reader :block, :data, :ctx, :errors
+          attr_reader :data, :ctx, :errors
+
+          def self.match(data)
+            return data unless data.is_a?(String) && data.include?(PREFIX)
+
+            data.gsub(PATTERN) do
+              yield ::Regexp.last_match(1), ::Regexp.last_match(2)
+            end
+          end
 
           def initialize(block, data, ctx)
             @block = block
@@ -43,16 +51,18 @@ def value
             @value
           end
 
-          def self.match(data)
-            return data unless data.is_a?(String) && data.include?(PREFIX)
+          def length
+            block.length
+          end
 
-            data.gsub(PATTERN) do
-              yield ::Regexp.last_match(1), ::Regexp.last_match(2)
-            end
+          def to_s
+            block
           end
 
           private
 
+          attr_reader :block
+
           # We expect the block data to be a string with one or more entities delimited by pipes:
           # <access> | <function1> | <function2> | ... <functionN>
           def evaluate!
diff --git a/lib/gitlab/ci/config/interpolation/template.rb b/lib/gitlab/ci/config/interpolation/template.rb
index 2a0f98480024dce10dcea4dd7551bbffdebc06f3..697be852c81ba276ccc1aaedf577f5e3bd0a56a4 100644
--- a/lib/gitlab/ci/config/interpolation/template.rb
+++ b/lib/gitlab/ci/config/interpolation/template.rb
@@ -10,6 +10,8 @@ class Template
           attr_reader :blocks, :ctx
 
           MAX_BLOCKS = 10_000
+          BLOCK_PATTERN = /(?<block>\$\[\[\s*(?<data>\S{1}.*?\S{1})\s*\]\])/
+          BLOCK_PREFIX = '$[['
 
           def initialize(config, ctx)
             @config = Interpolation::Config.fabricate(config)
@@ -39,20 +41,73 @@ def interpolated
           private
 
           def interpolate!
-            @result = @config.replace! do |data|
+            @result = @config.replace! do |node|
               break if @errors.any?
 
-              Interpolation::Block.match(data) do |block, data|
-                block = (@blocks[block] ||= Interpolation::Block.new(block, data, ctx))
-
-                break @errors.push('too many interpolation blocks') if @blocks.size > MAX_BLOCKS
-                break unless block.valid?
-
-                block.value
+              if Feature.enabled?(:ci_fix_input_types, Feature.current_request, type: :gitlab_com_derisk)
+                interpolate_with_fixed_types!(node)
+              else
+                legacy_interpolate!(node)
               end
             end
           end
           strong_memoize_attr :interpolate!
+
+          def interpolate_with_fixed_types!(node)
+            return node unless node_might_contain_interpolation_block?(node)
+
+            matches = node.scan(BLOCK_PATTERN)
+            return node if matches.empty?
+
+            blocks = interpolate_blocks(matches)
+            return unless @errors.none? && blocks.present?
+
+            get_interpolated_node_content!(node, blocks)
+          end
+
+          def legacy_interpolate!(node)
+            Interpolation::Block.match(node) do |block, data|
+              block = (@blocks[block] ||= Interpolation::Block.new(block, data, ctx))
+
+              break @errors.push('too many interpolation blocks') if @blocks.size > MAX_BLOCKS
+              break unless block.valid?
+
+              block.value
+            end
+          end
+
+          def node_might_contain_interpolation_block?(node)
+            node.is_a?(String) && node.include?(BLOCK_PREFIX)
+          end
+
+          def interpolate_blocks(matches)
+            matches.map do |match, data|
+              block = (@blocks[match] ||= Interpolation::Block.new(match, data, ctx))
+
+              break @errors.push('too many interpolation blocks') if @blocks.size > MAX_BLOCKS
+              break unless block.valid?
+
+              block
+            end
+          end
+
+          def get_interpolated_node_content!(node, blocks)
+            if used_inside_a_string?(node, blocks)
+              interpolate_string_node!(node, blocks)
+            else
+              blocks.first.value
+            end
+          end
+
+          def interpolate_string_node!(node, blocks)
+            blocks.reduce(node) do |interpolated_node, block|
+              interpolated_node.gsub(block.to_s, block.value.to_s)
+            end
+          end
+
+          def used_inside_a_string?(node, blocks)
+            blocks.count > 1 || node.length != blocks.first.length
+          end
         end
       end
     end
diff --git a/spec/lib/gitlab/ci/config/interpolation/block_spec.rb b/spec/lib/gitlab/ci/config/interpolation/block_spec.rb
index 7b9111f11d373b52bf60cbe41bf07ec090fa37ad..02bbbcb5a17acac443b08f95460be6169ffbd90b 100644
--- a/spec/lib/gitlab/ci/config/interpolation/block_spec.rb
+++ b/spec/lib/gitlab/ci/config/interpolation/block_spec.rb
@@ -109,4 +109,16 @@
       end
     end
   end
+
+  describe '#to_s' do
+    it 'returns the interpolation block' do
+      expect(subject.to_s).to eq(block)
+    end
+  end
+
+  describe '#length' do
+    it 'returns the length of the interpolation block' do
+      expect(subject.length).to eq(block.length)
+    end
+  end
 end
diff --git a/spec/lib/gitlab/ci/config/interpolation/template_spec.rb b/spec/lib/gitlab/ci/config/interpolation/template_spec.rb
index c7d8882255848a43cb2c4a5d16d74115e885759f..647c698edb7979481841218c9f96840be4be90b7 100644
--- a/spec/lib/gitlab/ci/config/interpolation/template_spec.rb
+++ b/spec/lib/gitlab/ci/config/interpolation/template_spec.rb
@@ -1,6 +1,6 @@
 # frozen_string_literal: true
 
-require 'fast_spec_helper'
+require 'spec_helper'
 
 RSpec.describe Gitlab::Ci::Config::Interpolation::Template, feature_category: :pipeline_composition do
   subject { described_class.new(YAML.safe_load(config), ctx) }
@@ -13,16 +13,18 @@
 
     $[[ inputs.key ]]:
       name: $[[ inputs.key ]]
-      script: my-value
+      parallel: $[[ inputs.parallel ]]
+      allow_failure: $[[ inputs.allow_failure ]]
+      script: 'echo "This job makes $[[ inputs.parallel ]] jobs for the $[[ inputs.env ]] env"'
     CFG
   end
 
   let(:ctx) do
-    { inputs: { env: 'dev', key: 'abc' } }
+    { inputs: { allow_failure: true, env: 'dev', key: 'abc', parallel: 6 } }
   end
 
   it 'collects interpolation blocks' do
-    expect(subject.size).to eq 2
+    expect(subject.size).to eq 4
   end
 
   it 'interpolates the values properly' do
@@ -33,10 +35,32 @@
 
     abc:
       name: abc
-      script: my-value
+      parallel: 6
+      allow_failure: true
+      script: 'echo "This job makes 6 jobs for the dev env"'
     RESULT
   end
 
+  context 'when ci_fix_input_types is disabled' do
+    before do
+      stub_feature_flags(ci_fix_input_types: false)
+    end
+
+    it 'interpolates all values as strings' do
+      expect(subject.interpolated).to eq YAML.safe_load <<~RESULT
+      test:
+        spec:
+          env: dev
+
+      abc:
+        name: abc
+        parallel: '6'
+        allow_failure: 'true'
+        script: 'echo "This job makes 6 jobs for the dev env"'
+      RESULT
+    end
+  end
+
   context 'when interpolation can not be performed' do
     let(:config) { '$[[ xxx.yyy ]]: abc' }
 
diff --git a/spec/lib/gitlab/ci/config/yaml/loader_spec.rb b/spec/lib/gitlab/ci/config/yaml/loader_spec.rb
index f8563533b04c13c03e159d7b8d51e5ba28877b34..2b1b85baeaead4b8273d72615a0ff45ee99927c5 100644
--- a/spec/lib/gitlab/ci/config/yaml/loader_spec.rb
+++ b/spec/lib/gitlab/ci/config/yaml/loader_spec.rb
@@ -16,7 +16,7 @@
             'echo "Building with clang and optimization level 3"',
             'echo "1.0.0"'
           ],
-          parallel: '2'
+          parallel: 2
         },
         'my-job-test': {
           stage: 'build',
@@ -24,7 +24,7 @@
             'echo "Testing with pytest"',
             'if [ true == true ]; then echo "Coverage is enabled"; fi'
           ],
-          allow_failure: 'false'
+          allow_failure: false
         },
         'my-job-deploy': {
           stage: 'build',