diff --git a/lib/config/migration.ts b/lib/config/migration.ts
index ddad631b66a5078de50099f51ff29964c54073c2..2d7fd47f075007cd964abdf0b6e1116537bced5e 100644
--- a/lib/config/migration.ts
+++ b/lib/config/migration.ts
@@ -5,7 +5,7 @@ import { logger } from '../logger';
 import { clone } from '../util/clone';
 import { regEx } from '../util/regex';
 import { getGlobalConfig } from './global';
-import { applyMigrations } from './migrations';
+import { MigrationsService } from './migrations';
 import { getOptions } from './options';
 import { removedPresets } from './presets/common';
 import type {
@@ -20,23 +20,6 @@ const options = getOptions();
 
 let optionTypes: Record<string, RenovateOptions['type']>;
 
-const removedOptions = [
-  'maintainYarnLock',
-  'yarnCacheFolder',
-  'yarnMaintenanceBranchName',
-  'yarnMaintenanceCommitMessage',
-  'yarnMaintenancePrTitle',
-  'yarnMaintenancePrBody',
-  'groupBranchName',
-  'groupBranchName',
-  'groupCommitMessage',
-  'groupPrTitle',
-  'groupPrBody',
-  'statusCheckVerify',
-  'lazyGrouping',
-  'supportPolicy',
-];
-
 // Returns a migrated config
 export function migrateConfig(
   config: RenovateConfig,
@@ -50,7 +33,8 @@ export function migrateConfig(
         optionTypes[option.name] = option.type;
       });
     }
-    const migratedConfig = clone(config) as MigratedRenovateConfig;
+    const newConfig = MigrationsService.run(config);
+    const migratedConfig = clone(newConfig) as MigratedRenovateConfig;
     const depTypes = [
       'dependencies',
       'devDependencies',
@@ -59,11 +43,8 @@ export function migrateConfig(
       'peerDependencies',
     ];
     const { migratePresets } = getGlobalConfig();
-    applyMigrations(config, migratedConfig);
     for (const [key, val] of Object.entries(config)) {
-      if (removedOptions.includes(key)) {
-        delete migratedConfig[key];
-      } else if (key === 'pathRules') {
+      if (key === 'pathRules') {
         if (is.array(val)) {
           migratedConfig.packageRules = is.array(migratedConfig.packageRules)
             ? migratedConfig.packageRules
@@ -85,13 +66,6 @@ export function migrateConfig(
           migratedConfig[newKey] = true;
         }
         delete migratedConfig[key];
-      } else if (key === 'gomodTidy') {
-        if (val) {
-          migratedConfig.postUpdateOptions =
-            migratedConfig.postUpdateOptions || [];
-          migratedConfig.postUpdateOptions.push('gomodTidy');
-        }
-        delete migratedConfig.gomodTidy;
       } else if (key === 'semanticCommits') {
         if (val === true) {
           migratedConfig.semanticCommits = 'enabled';
@@ -211,16 +185,6 @@ export function migrateConfig(
         if (val === false) {
           migratedConfig.rebaseWhen = 'never';
         }
-      } else if (key === 'exposeEnv') {
-        migratedConfig.exposeAllEnv = val;
-        delete migratedConfig.exposeEnv;
-      } else if (key === 'trustLevel') {
-        delete migratedConfig.trustLevel;
-        if (val === 'high') {
-          migratedConfig.allowCustomCrateRegistries ??= true;
-          migratedConfig.allowScripts ??= true;
-          migratedConfig.exposeAllEnv ??= true;
-        }
       } else if (key === 'ignoreNpmrcFile') {
         delete migratedConfig.ignoreNpmrcFile;
         if (!is.string(migratedConfig.npmrc)) {
@@ -305,9 +269,6 @@ export function migrateConfig(
           }
         }
         delete migratedConfig.unpublishSafe;
-      } else if (key === 'versionScheme') {
-        migratedConfig.versioning = val;
-        delete migratedConfig.versionScheme;
       } else if (
         key === 'automergeType' &&
         is.string(val) &&
@@ -322,25 +283,16 @@ export function migrateConfig(
         migratedConfig.major = migratedConfig.major || {};
         migratedConfig.major.automerge = !!val;
         delete migratedConfig[key];
-      } else if (key === 'multipleMajorPrs') {
-        delete migratedConfig.multipleMajorPrs;
-        migratedConfig.separateMultipleMajor = val;
       } else if (key === 'renovateFork' && is.boolean(val)) {
         delete migratedConfig.renovateFork;
         migratedConfig.includeForks = val;
       } else if (key === 'separateMajorReleases') {
         delete migratedConfig.separateMultipleMajor;
         migratedConfig.separateMajorMinor = val;
-      } else if (key === 'separatePatchReleases') {
-        delete migratedConfig.separatePatchReleases;
-        migratedConfig.separateMinorPatch = val;
       } else if (key === 'automergePatch') {
         migratedConfig.patch = migratedConfig.patch || {};
         migratedConfig.patch.automerge = !!val;
         delete migratedConfig[key];
-      } else if (key === 'ignoreNodeModules') {
-        delete migratedConfig.ignoreNodeModules;
-        migratedConfig.ignorePaths = val ? ['node_modules/'] : [];
       } else if (
         key === 'automerge' &&
         is.string(val) &&
@@ -374,9 +326,6 @@ export function migrateConfig(
           migratedConfig.packages
         );
         delete migratedConfig.packages;
-      } else if (key === 'excludedPackageNames') {
-        migratedConfig.excludePackageNames = val;
-        delete migratedConfig.excludedPackageNames;
       } else if (key === 'packageName') {
         migratedConfig.packageNames = [val];
         delete migratedConfig.packageName;
@@ -541,8 +490,6 @@ export function migrateConfig(
             migratedConfig.suppressNotifications || [];
           migratedConfig.suppressNotifications.push('deprecationWarningIssues');
         }
-      } else if (key === 'binarySource' && val === 'auto') {
-        migratedConfig.binarySource = 'global';
       } else if (key === 'composerIgnorePlatformReqs') {
         if (val === true) {
           migratedConfig.composerIgnorePlatformReqs = [];
diff --git a/lib/config/migrations/base/abstract-migration.ts b/lib/config/migrations/base/abstract-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56d6fb7548bf227439bcc7a45bc92187438a5659
--- /dev/null
+++ b/lib/config/migrations/base/abstract-migration.ts
@@ -0,0 +1,26 @@
+import type { RenovateConfig } from '../../types';
+import type { Migration } from '../types';
+
+export abstract class AbstractMigration implements Migration {
+  readonly propertyName: string;
+
+  protected readonly originalConfig: RenovateConfig;
+
+  protected readonly migratedConfig: RenovateConfig;
+
+  constructor(
+    propertyName: string,
+    originalConfig: RenovateConfig,
+    migratedConfig: RenovateConfig
+  ) {
+    this.propertyName = propertyName;
+    this.originalConfig = originalConfig;
+    this.migratedConfig = migratedConfig;
+  }
+
+  abstract run(): void;
+
+  protected delete(property: string): void {
+    delete this.migratedConfig[property];
+  }
+}
diff --git a/lib/config/migrations/base/remove-property-migration.ts b/lib/config/migrations/base/remove-property-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b2ad2496963b21882ed81035f96e7c5de8b29f6
--- /dev/null
+++ b/lib/config/migrations/base/remove-property-migration.ts
@@ -0,0 +1,7 @@
+import { AbstractMigration } from './abstract-migration';
+
+export class RemovePropertyMigration extends AbstractMigration {
+  override run(): void {
+    this.delete(this.propertyName);
+  }
+}
diff --git a/lib/config/migrations/base/rename-property-migration.ts b/lib/config/migrations/base/rename-property-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c317634be40ea44ded1bd7ef6486da72703d75fe
--- /dev/null
+++ b/lib/config/migrations/base/rename-property-migration.ts
@@ -0,0 +1,23 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from './abstract-migration';
+
+export class RenamePropertyMigration extends AbstractMigration {
+  protected readonly newPropertyName: string;
+
+  constructor(
+    deprecatedPropertyName: string,
+    newPropertyName: string,
+    originalConfig: RenovateConfig,
+    migratedConfig: RenovateConfig
+  ) {
+    super(deprecatedPropertyName, originalConfig, migratedConfig);
+    this.newPropertyName = newPropertyName;
+  }
+
+  override run(): void {
+    this.delete(this.propertyName);
+
+    this.migratedConfig[this.newPropertyName] =
+      this.originalConfig[this.propertyName];
+  }
+}
diff --git a/lib/config/migrations/custom/binary-source-migration.spec.ts b/lib/config/migrations/custom/binary-source-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22ef956ba40e2bf113cd9b1fb573eb3faafa766c
--- /dev/null
+++ b/lib/config/migrations/custom/binary-source-migration.spec.ts
@@ -0,0 +1,11 @@
+import { MigrationsService } from '../migrations-service';
+
+describe('config/migrations/custom/binary-source-migration', () => {
+  it('should migrate "auto" to "global"', () => {
+    const migratedConfig = MigrationsService.run({
+      binarySource: 'auto',
+    });
+
+    expect(migratedConfig.binarySource).toBe('global');
+  });
+});
diff --git a/lib/config/migrations/custom/binary-source-migration.ts b/lib/config/migrations/custom/binary-source-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1452897133193b76a73572b52e3c6cdf340cc92b
--- /dev/null
+++ b/lib/config/migrations/custom/binary-source-migration.ts
@@ -0,0 +1,14 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class BinarySourceMigration extends AbstractMigration {
+  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
+    super('binarySource', originalConfig, migratedConfig);
+  }
+
+  override run(): void {
+    if (this.originalConfig.binarySource === 'auto') {
+      this.migratedConfig.binarySource = 'global';
+    }
+  }
+}
diff --git a/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts b/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f0c7be274fe469fae05c914d18c7240e5061f996
--- /dev/null
+++ b/lib/config/migrations/custom/go-mod-tidy-migration.spec.ts
@@ -0,0 +1,31 @@
+import { MigrationsService } from '../migrations-service';
+
+describe('config/migrations/custom/go-mod-tidy-migration', () => {
+  it('should add postUpdateOptions option when true', () => {
+    const migratedConfig = MigrationsService.run({
+      gomodTidy: true,
+      postUpdateOptions: ['test'],
+    });
+
+    expect(migratedConfig).not.toHaveProperty('gomodTidy');
+    expect(migratedConfig.postUpdateOptions).toEqual(['test', 'gomodTidy']);
+  });
+
+  it('should handle case when postUpdateOptions is not defined ', () => {
+    const migratedConfig = MigrationsService.run({
+      gomodTidy: true,
+    });
+
+    expect(migratedConfig).not.toHaveProperty('gomodTidy');
+    expect(migratedConfig.postUpdateOptions).toEqual(['gomodTidy']);
+  });
+
+  it('should only remove when false', () => {
+    const migratedConfig = MigrationsService.run({
+      gomodTidy: false,
+    });
+
+    expect(migratedConfig).not.toHaveProperty('gomodTidy');
+    expect(migratedConfig).not.toHaveProperty('postUpdateOptions');
+  });
+});
diff --git a/lib/config/migrations/custom/go-mod-tidy-migration.ts b/lib/config/migrations/custom/go-mod-tidy-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b655489da2116a85303fcd9c785a854b747f993d
--- /dev/null
+++ b/lib/config/migrations/custom/go-mod-tidy-migration.ts
@@ -0,0 +1,19 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class GoModTidyMigration extends AbstractMigration {
+  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
+    super('gomodTidy', originalConfig, migratedConfig);
+  }
+
+  override run(): void {
+    const { gomodTidy, postUpdateOptions } = this.originalConfig;
+
+    this.delete(this.propertyName);
+
+    if (gomodTidy) {
+      this.migratedConfig.postUpdateOptions ??= postUpdateOptions ?? [];
+      this.migratedConfig.postUpdateOptions.push('gomodTidy');
+    }
+  }
+}
diff --git a/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts b/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f2d4d000a8129f3efcc1e71cfe849db00bf6f3e
--- /dev/null
+++ b/lib/config/migrations/custom/ignore-node-modules-migration.spec.ts
@@ -0,0 +1,11 @@
+import { MigrationsService } from '../migrations-service';
+
+describe('config/migrations/custom/ignore-node-modules-migration', () => {
+  it('should migrate to ignorePaths', () => {
+    const migratedConfig = MigrationsService.run({
+      ignoreNodeModules: true,
+    });
+
+    expect(migratedConfig.ignorePaths).toEqual(['node_modules/']);
+  });
+});
diff --git a/lib/config/migrations/custom/ignore-node-modules-migration.ts b/lib/config/migrations/custom/ignore-node-modules-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..976b53df3cd0e027066e3595b19f3c09e86974e8
--- /dev/null
+++ b/lib/config/migrations/custom/ignore-node-modules-migration.ts
@@ -0,0 +1,16 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class IgnoreNodeModulesMigration extends AbstractMigration {
+  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
+    super('ignoreNodeModules', originalConfig, migratedConfig);
+  }
+
+  override run(): void {
+    this.delete(this.propertyName);
+
+    this.migratedConfig.ignorePaths = this.originalConfig.ignoreNodeModules
+      ? ['node_modules/']
+      : [];
+  }
+}
diff --git a/lib/config/migrations/custom/required-status-checks-migration.spec.ts b/lib/config/migrations/custom/required-status-checks-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fdd67820869e04245cbbd917d65af78a8c7e937
--- /dev/null
+++ b/lib/config/migrations/custom/required-status-checks-migration.spec.ts
@@ -0,0 +1,12 @@
+import { MigrationsService } from '../migrations-service';
+
+describe('config/migrations/custom/required-status-checks-migration', () => {
+  it('should migrate requiredStatusChecks=null to ignoreTests=true', () => {
+    const migratedConfig = MigrationsService.run({
+      requiredStatusChecks: null,
+    });
+
+    expect(migratedConfig).not.toHaveProperty('requiredStatusChecks');
+    expect(migratedConfig.ignoreTests).toBeTrue();
+  });
+});
diff --git a/lib/config/migrations/custom/required-status-checks-migration.ts b/lib/config/migrations/custom/required-status-checks-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cd06869c20e18caea07bef8d30d9920d4744e349
--- /dev/null
+++ b/lib/config/migrations/custom/required-status-checks-migration.ts
@@ -0,0 +1,16 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class RequiredStatusChecksMigration extends AbstractMigration {
+  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
+    super('requiredStatusChecks', originalConfig, migratedConfig);
+  }
+
+  override run(): void {
+    this.delete(this.propertyName);
+
+    if (this.originalConfig.requiredStatusChecks === null) {
+      this.migratedConfig.ignoreTests = true;
+    }
+  }
+}
diff --git a/lib/config/migrations/custom/trust-level-migration.spec.ts b/lib/config/migrations/custom/trust-level-migration.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5fac9753ea1fba9081e94d2c9c4cbe9ed8fd5c6e
--- /dev/null
+++ b/lib/config/migrations/custom/trust-level-migration.spec.ts
@@ -0,0 +1,26 @@
+import { MigrationsService } from '../migrations-service';
+
+describe('config/migrations/custom/trust-level-migration', () => {
+  it('should handle hight level', () => {
+    const migratedConfig = MigrationsService.run({
+      trustLevel: 'high',
+    });
+
+    expect(migratedConfig.allowCustomCrateRegistries).toBeTrue();
+    expect(migratedConfig.allowScripts).toBeTrue();
+    expect(migratedConfig.exposeAllEnv).toBeTrue();
+  });
+
+  it('should not rewrite provided properties', () => {
+    const migratedConfig = MigrationsService.run({
+      allowCustomCrateRegistries: false,
+      allowScripts: false,
+      exposeAllEnv: false,
+      trustLevel: 'high',
+    });
+
+    expect(migratedConfig.allowCustomCrateRegistries).toBeFalse();
+    expect(migratedConfig.allowScripts).toBeFalse();
+    expect(migratedConfig.exposeAllEnv).toBeFalse();
+  });
+});
diff --git a/lib/config/migrations/custom/trust-level-migration.ts b/lib/config/migrations/custom/trust-level-migration.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b9e09077f4573936191864cde8caca1c8d1d0fd7
--- /dev/null
+++ b/lib/config/migrations/custom/trust-level-migration.ts
@@ -0,0 +1,21 @@
+import type { RenovateConfig } from '../../types';
+import { AbstractMigration } from '../base/abstract-migration';
+
+export class TrustLevelMigration extends AbstractMigration {
+  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
+    super('trustLevel', originalConfig, migratedConfig);
+  }
+
+  override run(): void {
+    this.delete(this.propertyName);
+
+    if (this.originalConfig.trustLevel === 'high') {
+      this.migratedConfig.allowCustomCrateRegistries =
+        this.originalConfig.allowCustomCrateRegistries ?? true;
+      this.migratedConfig.allowScripts =
+        this.originalConfig.allowScripts ?? true;
+      this.migratedConfig.exposeAllEnv =
+        this.originalConfig.exposeAllEnv ?? true;
+    }
+  }
+}
diff --git a/lib/config/migrations/index.ts b/lib/config/migrations/index.ts
index 1f6c961c2930312e0a3fee3aa328de59b0ef93e3..2831c79f22e53b869c7a902d2078d690544982a9 100644
--- a/lib/config/migrations/index.ts
+++ b/lib/config/migrations/index.ts
@@ -1,18 +1 @@
-import { RenovateConfig } from '../types';
-import type { Migration } from './migration';
-import { RequiredStatusChecksMigration } from './required-status-checks-migration';
-
-export function applyMigrations(
-  originalConfig: RenovateConfig,
-  migratedConfig: RenovateConfig
-): RenovateConfig {
-  const migrations: Migration[] = [
-    new RequiredStatusChecksMigration(originalConfig, migratedConfig),
-  ];
-
-  for (const migration of migrations) {
-    migration.migrate();
-  }
-
-  return migratedConfig;
-}
+export { MigrationsService } from './migrations-service';
diff --git a/lib/config/migrations/migration.ts b/lib/config/migrations/migration.ts
deleted file mode 100644
index 616b96caeaeb175ab5a201cab7299dbf44f198b0..0000000000000000000000000000000000000000
--- a/lib/config/migrations/migration.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { RenovateConfig } from '../types';
-
-export abstract class Migration {
-  protected readonly originalConfig: RenovateConfig;
-
-  protected readonly migratedConfig: RenovateConfig;
-
-  constructor(originalConfig: RenovateConfig, migratedConfig: RenovateConfig) {
-    this.originalConfig = originalConfig;
-    this.migratedConfig = migratedConfig;
-  }
-
-  abstract migrate(): void;
-
-  protected delete(property: string): void {
-    delete this.migratedConfig[property];
-  }
-}
diff --git a/lib/config/migrations/migrations-service.spec.ts b/lib/config/migrations/migrations-service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..222da9a9ca05ae859705a806cabd8b60ee82bf6f
--- /dev/null
+++ b/lib/config/migrations/migrations-service.spec.ts
@@ -0,0 +1,45 @@
+import type { RenovateConfig } from '../types';
+import { MigrationsService } from './migrations-service';
+
+describe('config/migrations/migrations-service', () => {
+  it('should remove deprecated properties', () => {
+    for (const property of MigrationsService.removedProperties) {
+      const originalConfig: RenovateConfig = {
+        [property]: 'test',
+      };
+
+      const migratedConfig = MigrationsService.run(originalConfig);
+      expect(migratedConfig).not.toHaveProperty(property);
+    }
+  });
+
+  it('should rename renamed properties', () => {
+    for (const [
+      oldPropertyName,
+      newPropertyName,
+    ] of MigrationsService.renamedProperties.entries()) {
+      const originalConfig: RenovateConfig = {
+        [oldPropertyName]: 'test',
+      };
+
+      const migratedConfig = MigrationsService.run(originalConfig);
+      expect(migratedConfig).not.toHaveProperty(oldPropertyName);
+      expect(migratedConfig[newPropertyName]).toBe('test');
+    }
+  });
+
+  it('should save original order of properties', () => {
+    const originalConfig: RenovateConfig = {
+      exposeEnv: true,
+      versionScheme: 'test',
+      excludedPackageNames: ['test'],
+    };
+    const migratedConfig = MigrationsService.run(originalConfig);
+
+    const mappedProperties = Object.keys(originalConfig).map((property) =>
+      MigrationsService.renamedProperties.get(property)
+    );
+
+    expect(mappedProperties).toEqual(Object.keys(migratedConfig));
+  });
+});
diff --git a/lib/config/migrations/migrations-service.ts b/lib/config/migrations/migrations-service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d1cee89acb423ac030428961b94a9b6346c0dd9b
--- /dev/null
+++ b/lib/config/migrations/migrations-service.ts
@@ -0,0 +1,95 @@
+import type { RenovateConfig } from '../types';
+import { RemovePropertyMigration } from './base/remove-property-migration';
+import { RenamePropertyMigration } from './base/rename-property-migration';
+import { BinarySourceMigration } from './custom/binary-source-migration';
+import { GoModTidyMigration } from './custom/go-mod-tidy-migration';
+import { IgnoreNodeModulesMigration } from './custom/ignore-node-modules-migration';
+import { RequiredStatusChecksMigration } from './custom/required-status-checks-migration';
+import { TrustLevelMigration } from './custom/trust-level-migration';
+import type { Migration } from './types';
+
+export class MigrationsService {
+  static readonly removedProperties: ReadonlySet<string> = new Set([
+    'gitFs',
+    'groupBranchName',
+    'groupCommitMessage',
+    'groupPrBody',
+    'groupPrTitle',
+    'lazyGrouping',
+    'maintainYarnLock',
+    'statusCheckVerify',
+    'supportPolicy',
+    'yarnCacheFolder',
+    'yarnMaintenanceBranchName',
+    'yarnMaintenanceCommitMessage',
+    'yarnMaintenancePrBody',
+    'yarnMaintenancePrTitle',
+  ]);
+
+  static readonly renamedProperties: ReadonlyMap<string, string> = new Map([
+    ['exposeEnv', 'exposeAllEnv'],
+    ['separatePatchReleases', 'separateMinorPatch'],
+    ['multipleMajorPrs', 'separateMultipleMajor'],
+    ['excludedPackageNames', 'excludePackageNames'],
+    ['versionScheme', 'versioning'],
+  ]);
+
+  static run(originalConfig: RenovateConfig): RenovateConfig {
+    const migratedConfig: RenovateConfig = {};
+    const migrations = MigrationsService.getMigrations(
+      originalConfig,
+      migratedConfig
+    );
+
+    for (const [key, value] of Object.entries(originalConfig)) {
+      migratedConfig[key] ??= value;
+      const migration = migrations.find((item) => item.propertyName === key);
+      migration?.run();
+    }
+
+    return migratedConfig;
+  }
+
+  private static getMigrations(
+    originalConfig: RenovateConfig,
+    migratedConfig: RenovateConfig
+  ): Migration[] {
+    const migrations: Migration[] = [];
+
+    for (const propertyName of MigrationsService.removedProperties) {
+      migrations.push(
+        new RemovePropertyMigration(
+          propertyName,
+          originalConfig,
+          migratedConfig
+        )
+      );
+    }
+
+    for (const [
+      oldPropertyName,
+      newPropertyName,
+    ] of MigrationsService.renamedProperties.entries()) {
+      migrations.push(
+        new RenamePropertyMigration(
+          oldPropertyName,
+          newPropertyName,
+          originalConfig,
+          migratedConfig
+        )
+      );
+    }
+
+    migrations.push(new BinarySourceMigration(originalConfig, migratedConfig));
+    migrations.push(
+      new IgnoreNodeModulesMigration(originalConfig, migratedConfig)
+    );
+    migrations.push(
+      new RequiredStatusChecksMigration(originalConfig, migratedConfig)
+    );
+    migrations.push(new TrustLevelMigration(originalConfig, migratedConfig));
+    migrations.push(new GoModTidyMigration(originalConfig, migratedConfig));
+
+    return migrations;
+  }
+}
diff --git a/lib/config/migrations/required-status-checks-migration.spec.ts b/lib/config/migrations/required-status-checks-migration.spec.ts
deleted file mode 100644
index afe9019ee573f3baee912d97e2d2c520dc450eae..0000000000000000000000000000000000000000
--- a/lib/config/migrations/required-status-checks-migration.spec.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { RequiredStatusChecksMigration } from './required-status-checks-migration';
-
-describe('config/migrations/required-status-checks-migration', () => {
-  it('should migrate requiredStatusChecks=null to ignoreTests=true', () => {
-    const originalConfig: any = {
-      requiredStatusChecks: null,
-    };
-    const migratedConfig: any = {
-      requiredStatusChecks: null,
-    };
-    const migration = new RequiredStatusChecksMigration(
-      originalConfig,
-      migratedConfig
-    );
-
-    expect(migratedConfig.requiredStatusChecks).toBeNull();
-    migration.migrate();
-    expect(migratedConfig.requiredStatusChecks).toBeUndefined();
-    expect(migratedConfig.ignoreTests).toBeTrue();
-  });
-});
diff --git a/lib/config/migrations/required-status-checks-migration.ts b/lib/config/migrations/required-status-checks-migration.ts
deleted file mode 100644
index 3f6d64e28fe7cffa36dbc7e65b6604ee164127cf..0000000000000000000000000000000000000000
--- a/lib/config/migrations/required-status-checks-migration.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { Migration } from './migration';
-
-export class RequiredStatusChecksMigration extends Migration {
-  public migrate(): void {
-    this.delete('requiredStatusChecks');
-
-    if (this.originalConfig.requiredStatusChecks === null) {
-      this.migratedConfig.ignoreTests = true;
-    }
-  }
-}
diff --git a/lib/config/migrations/types.ts b/lib/config/migrations/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..93f2035ed60a6000cc4f04b7aa0f6bcd8258d7b9
--- /dev/null
+++ b/lib/config/migrations/types.ts
@@ -0,0 +1,4 @@
+export interface Migration {
+  readonly propertyName: string;
+  run(): void;
+}