diff --git a/enum_field.services.yml b/enum_field.services.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d8f2199b9746e4c5d180136d57c1c0b17f96c9e0
--- /dev/null
+++ b/enum_field.services.yml
@@ -0,0 +1,8 @@
+services:
+  enum_field.migration:
+    class: Drupal\enum_field\Migration
+    arguments:
+      - '@config.factory'
+      - '@entity_field.manager'
+      - '@entity.last_installed_schema.repository'
+  Drupal\enum_field\Migration: '@enum_field.migration'
diff --git a/src/Migration.php b/src/Migration.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbe0fce7a183c6c1e3fb33a175ded9331a7f6045
--- /dev/null
+++ b/src/Migration.php
@@ -0,0 +1,154 @@
+<?php
+
+namespace Drupal\enum_field;
+
+use Drupal\Core\Config\Config;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+
+/**
+ * Helper methods to migrate between list and enum field types.
+ */
+class Migration {
+
+  protected const FIELD_TYPE_MAP = [
+    'enum_integer' => 'list_integer',
+    'enum_string' => 'list_string',
+  ];
+
+  /**
+   * Constructor.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
+   *   The config factory service.
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entityFieldManager
+   *   The entity field manager service.
+   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository
+   *   The entity last installed schema repository service.
+   */
+  public function __construct(
+    protected ConfigFactoryInterface $configFactory,
+    protected EntityFieldManagerInterface $entityFieldManager,
+    protected EntityLastInstalledSchemaRepositoryInterface $entityLastInstalledSchemaRepository,
+  ) {
+  }
+
+  /**
+   * Migrates an enum field to a list field.
+   *
+   * @param string $entityTypeId
+   *   The entity type ID.
+   * @param string $fieldName
+   *   The field name.
+   *
+   * @throws \Exception
+   *    Thrown when the field is not a list field.
+   */
+  public function migrateEnumField(string $entityTypeId, string $fieldName): void {
+    $fieldStorageConfig = $this->configFactory->getEditable("field.storage.$entityTypeId.$fieldName");
+    $sourceFieldType = $fieldStorageConfig->get('type');
+    $targetFieldType = static::FIELD_TYPE_MAP[$sourceFieldType] ?? NULL;
+
+    if ($sourceFieldType === $targetFieldType) {
+      // Already migrated
+      return;
+    }
+
+    if ($targetFieldType === NULL) {
+      throw new \Exception('Not an enum field');
+    }
+
+    $this->doMigrateField(
+      entityTypeId: $entityTypeId,
+      fieldName: $fieldName,
+      fieldStorageConfig: $fieldStorageConfig,
+      sourceFieldType: $sourceFieldType,
+      targetFieldType: $targetFieldType,
+      targetModule: 'options',
+    );
+  }
+
+  /**
+   * Migrates a list field to an enum field.
+   *
+   * @param string $entityTypeId
+   *   The entity type ID.
+   * @param string $fieldName
+   *   The field name.
+   *
+   * @throws \Exception
+   *   Thrown when the field is not a list field.
+   */
+  public function migrateListField(string $entityTypeId, string $fieldName): void {
+    $fieldStorageConfig = $this->configFactory->getEditable("field.storage.$entityTypeId.$fieldName");
+    $sourceFieldType = $fieldStorageConfig->get('type');
+    $targetFieldType = array_reverse(static::FIELD_TYPE_MAP)[$sourceFieldType] ?? NULL;
+
+    if ($sourceFieldType === $targetFieldType) {
+      // Already migrated
+      return;
+    }
+
+    if ($targetFieldType === NULL) {
+      throw new \Exception('Not a list field');
+    }
+
+    $this->doMigrateField(
+      entityTypeId: $entityTypeId,
+      fieldName: $fieldName,
+      fieldStorageConfig: $fieldStorageConfig,
+      sourceFieldType: $sourceFieldType,
+      targetFieldType: $targetFieldType,
+      targetModule: 'enum_field',
+    );
+  }
+
+    /**
+     * Migrates a field.
+     *
+     * @param string $entityTypeId
+     *   The entity type ID.
+     * @param string $fieldName
+     *   The field name.
+     * @param \Drupal\Core\Config\Config $fieldStorageConfig
+     *   The field storage config.
+     * @param string $sourceFieldType
+     *   The source field type.
+     * @param string $targetFieldType
+     *   The target field type.
+     * @param string $targetModule
+     *   The target module.
+     */
+  protected function doMigrateField(string $entityTypeId, string $fieldName, Config $fieldStorageConfig, string $sourceFieldType, string $targetFieldType, string $targetModule): void {
+    // Update the field storage.
+    /** @var \Drupal\field\Entity\FieldStorageConfig[] $schemaDefinitions */
+    $schemaDefinitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entityTypeId);
+    $schemaDefinitions[$fieldName]->set('type', $targetFieldType);
+    $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinitions($entityTypeId, $schemaDefinitions);
+
+    $this->entityFieldManager->clearCachedFieldDefinitions();
+
+    $fieldStorageConfig->set('type', $targetFieldType);
+    $fieldStorageConfig->set('module', $targetModule);
+    $fieldStorageConfig->save(TRUE);
+
+    FieldStorageConfig::loadByName($entityTypeId, $fieldName)->calculateDependencies()->save();
+
+    // Update the field instances.
+    $fieldMap = $this->entityFieldManager->getFieldMapByFieldType($sourceFieldType)[$entityTypeId][$fieldName];
+
+    foreach ($fieldMap['bundles'] as $bundle) {
+      $fieldConfig = $this->configFactory->getEditable("field.field.$entityTypeId.$bundle.$fieldName");
+      $fieldConfig->set('field_type', $targetFieldType);
+      $fieldConfig->save();
+
+      /** @var \Drupal\field\FieldConfigInterface $fieldConfig */
+      $fieldConfig = FieldConfig::loadByName($entityTypeId, $bundle, $fieldName);
+      $fieldConfig->calculateDependencies()->save();
+    }
+  }
+
+}