diff --git a/migrate_tools.drush.inc b/migrate_tools.drush.inc
index 73ddec5..8b366f2 100644
--- a/migrate_tools.drush.inc
+++ b/migrate_tools.drush.inc
@@ -12,6 +12,7 @@ use Drupal\migrate\Plugin\RequirementsInterface;
 use Drupal\migrate_plus\Entity\MigrationGroup;
 use Drupal\migrate_tools\DrushLogMigrateMessage;
 use Drupal\migrate_tools\MigrateExecutable;
+use Drupal\migrate_tools\MigrateBatchExecutable;
 
 /**
  * Implements hook_drush_command().
@@ -45,7 +46,6 @@ function migrate_tools_drush_command() {
       'group' => 'A comma-separated list of migration groups to import',
       'tag' => 'Name of the migration tag to import',
       'limit' => 'Limit on the number of items to process in each migration',
-      'feedback' => 'Frequency of progress messages, in items processed',
       'idlist' => 'Comma-separated list of IDs to import',
       'update' => ' In addition to processing unprocessed items from the source, update previously-imported items with the current data',
       'force' => 'Force an operation to run, even if all dependencies are not satisfied',
@@ -251,7 +251,7 @@ function drush_migrate_tools_migrate_import($migration_names = '') {
     return;
   }
 
-  foreach (['limit', 'feedback', 'idlist', 'update', 'force'] as $option) {
+  foreach (['limit', 'idlist', 'update', 'force'] as $option) {
     if (drush_get_option($option)) {
       $options[$option] = drush_get_option($option);
     }
@@ -298,9 +298,10 @@ function _drush_migrate_tools_execute_migration(MigrationInterface $migration, $
   if (!empty($options['update'])) {
     $migration->getIdMap()->prepareUpdate();
   }
-  $executable = new MigrateExecutable($migration, $log, $options);
+  $executable = new MigrateBatchExecutable($migration, $log, $options);
+  $executable->batchImport();
   // Function drush_op() provides --simulate support.
-  drush_op([$executable, 'import']);
+  drush_op('drush_backend_batch_process');
   if ($count = $executable->getFailedCount()) {
     // Nudge Drush to use a non-zero exit code.
     drush_set_error('MIGRATE_ERROR', dt('!name Migration - !count failed.', [
diff --git a/src/Commands/MigrateToolsCommands.php b/src/Commands/MigrateToolsCommands.php
index 976b3c6..2d086cf 100644
--- a/src/Commands/MigrateToolsCommands.php
+++ b/src/Commands/MigrateToolsCommands.php
@@ -13,6 +13,7 @@ use Drupal\migrate\Plugin\MigrationPluginManager;
 use Drupal\migrate\Plugin\RequirementsInterface;
 use Drupal\migrate_tools\Drush9LogMigrateMessage;
 use Drupal\migrate_tools\MigrateExecutable;
+use Drupal\migrate_tools\MigrateBatchExecutable;
 use Drush\Commands\DrushCommands;
 
 /**
@@ -229,7 +230,6 @@ class MigrateToolsCommands extends DrushCommands {
    * @option group A comma-separated list of migration groups to import
    * @option tag Name of the migration tag to import
    * @option limit Limit on the number of items to process in each migration
-   * @option feedback Frequency of progress messages, in items processed
    * @option idlist Comma-separated list of IDs to import
    * @option update  In addition to processing unprocessed items from the
    *   source, update previously-imported items with the current data
@@ -259,7 +259,7 @@ class MigrateToolsCommands extends DrushCommands {
    * @throws \Exception
    *   If there are not enough parameters to the command.
    */
-  public function import($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'limit' => NULL, 'feedback' => NULL, 'idlist' => NULL, 'update' => NULL, 'force' => NULL, 'execute-dependencies' => NULL]) {
+  public function import($migration_names = '', array $options = ['all' => NULL, 'group' => NULL, 'tag' => NULL, 'limit' => NULL, 'idlist' => NULL, 'update' => NULL, 'force' => NULL, 'execute-dependencies' => NULL]) {
     $group_names = $options['group'];
     $tag_names = $options['tag'];
     $all = $options['all'];
@@ -268,7 +268,7 @@ class MigrateToolsCommands extends DrushCommands {
       throw new \Exception(dt('You must specify --all, --group, --tag or one or more migration names separated by commas'));
     }
 
-    foreach (['limit', 'feedback', 'idlist', 'update', 'force', 'execute-dependencies'] as $option) {
+    foreach (['limit', 'idlist', 'update', 'force'] as $option) {
       if ($options[$option]) {
         $additional_options[$option] = $options[$option];
       }
@@ -688,9 +688,10 @@ class MigrateToolsCommands extends DrushCommands {
     if (!empty($options['update'])) {
       $migration->getIdMap()->prepareUpdate();
     }
-    $executable = new MigrateExecutable($migration, $this->getMigrateMessage(), $options);
+    $executable = new MigrateBatchExecutable($migration, $this->getMigrateMessage(), $options);
+    $executable->batchImport();
     // drush_op() provides --simulate support.
-    drush_op([$executable, 'import']);
+    drush_op('drush_backend_batch_process');
     $executed_migrations += [$migration_id => $migration_id];
     if ($count = $executable->getFailedCount()) {
       // Nudge Drush to use a non-zero exit code.
diff --git a/src/MigrateBatchExecutable.php b/src/MigrateBatchExecutable.php
index ee24f64..11c9819 100644
--- a/src/MigrateBatchExecutable.php
+++ b/src/MigrateBatchExecutable.php
@@ -103,6 +103,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
         'progress_message' => t('Migrating %migrate', ['%migrate' => $this->migration->label()]),
         'error_message' => t('An error occurred while migrating %migrate.', ['%migrate' => $this->migration->label()]),
         'finished' => '\Drupal\migrate_tools\MigrateBatchExecutable::batchFinishedImport',
+        'file' => drupal_get_path('module', 'migrate_tools') . '/src/MigrateBatchExecutable.php',
       ];
 
       batch_set($batch);
@@ -192,7 +193,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
         '@updated' => 0,
         '@failures' => 0,
         '@ignored' => 0,
-        '@name' => $migration->id(),
+        '@name' => $migration->id()
       ];
     }
 
@@ -212,7 +213,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
       '@updated' => $context['results'][$migration->id()]['@updated'] + $executable->getUpdatedCount(),
       '@failures' => $context['results'][$migration->id()]['@failures'] + $executable->getFailedCount(),
       '@ignored' => $context['results'][$migration->id()]['@ignored'] + $executable->getIgnoredCount(),
-      '@name' => $migration->id(),
+      '@name' => $migration->id()
     ];
 
     // Do some housekeeping.
@@ -225,10 +226,7 @@ class MigrateBatchExecutable extends MigrateExecutable {
       $context['sandbox']['counter'] = $context['results'][$migration->id()]['@numitems'];
       if ($context['sandbox']['counter'] <= $context['sandbox']['total']) {
         $context['finished'] = ((float) $context['sandbox']['counter'] / (float) $context['sandbox']['total']);
-        $context['message'] = t('Importing %migration (@percent%).', [
-          '%migration' => $migration->label(),
-          '@percent' => (int) ($context['finished'] * 100),
-        ]);
+        $context['message'] = $executable->progressMessage(FALSE);
       }
     }
 
diff --git a/src/MigrateExecutable.php b/src/MigrateExecutable.php
index 2282ab8..f6c46c2 100644
--- a/src/MigrateExecutable.php
+++ b/src/MigrateExecutable.php
@@ -2,19 +2,19 @@
 
 namespace Drupal\migrate_tools;
 
+use Drupal\migrate\Event\MigrateEvents;
+use Drupal\migrate\Event\MigrateImportEvent;
+use Drupal\migrate\Event\MigrateMapDeleteEvent;
+use Drupal\migrate\Event\MigrateMapSaveEvent;
 use Drupal\migrate\Event\MigratePreRowSaveEvent;
 use Drupal\migrate\Event\MigrateRollbackEvent;
 use Drupal\migrate\Event\MigrateRowDeleteEvent;
 use Drupal\migrate\MigrateExecutable as MigrateExecutableBase;
 use Drupal\migrate\MigrateMessageInterface;
-use Drupal\migrate\Plugin\MigrationInterface;
 use Drupal\migrate\MigrateSkipRowException;
 use Drupal\migrate\Plugin\MigrateIdMapInterface;
-use Drupal\migrate\Event\MigrateEvents;
+use Drupal\migrate\Plugin\MigrationInterface;
 use Drupal\migrate_plus\Event\MigrateEvents as MigratePlusEvents;
-use Drupal\migrate\Event\MigrateMapSaveEvent;
-use Drupal\migrate\Event\MigrateMapDeleteEvent;
-use Drupal\migrate\Event\MigrateImportEvent;
 use Drupal\migrate_plus\Event\MigratePrepareRowEvent;
 
 /**
@@ -243,11 +243,42 @@ class MigrateExecutable extends MigrateExecutableBase {
   public function onPostImport(MigrateImportEvent $event) {
     $migrate_last_imported_store = \Drupal::keyValue('migrate_last_imported');
     $migrate_last_imported_store->set($event->getMigration()->id(), round(microtime(TRUE) * 1000));
-    $this->progressMessage();
+    $this->progressMessageEmit();
     $this->removeListeners();
   }
 
   /**
+   * Generate a progress message.
+   *
+   * @param bool $done
+   *   Whether the import has fully finished
+   *
+   * @return \Drupal\Component\Render\MarkupInterface
+   *   The progress message to show to the end user
+   */
+  public function progressMessage($done = TRUE) {
+    $processed = $this->getProcessedCount();
+    if ($done) {
+      $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
+      $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
+    }
+    else {
+      $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
+      $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
+    }
+    return \Drupal::translation()->formatPlural($processed,
+      $singular_message, $plural_message,
+      [
+        '@numitems' => $processed,
+        '@created' => $this->getCreatedCount(),
+        '@updated' => $this->getUpdatedCount(),
+        '@failures' => $this->getFailedCount(),
+        '@ignored' => $this->getIgnoredCount(),
+        '@name' => $this->migration->id(),
+      ]);
+  }
+
+  /**
    * Clean up all our event listeners.
    */
   protected function removeListeners() {
@@ -264,27 +295,8 @@ class MigrateExecutable extends MigrateExecutableBase {
    * @param bool $done
    *   TRUE if this is the last items to process. Otherwise FALSE.
    */
-  protected function progressMessage($done = TRUE) {
-    $processed = $this->getProcessedCount();
-    if ($done) {
-      $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
-      $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - done with '@name'";
-    }
-    else {
-      $singular_message = "Processed 1 item (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
-      $plural_message = "Processed @numitems items (@created created, @updated updated, @failures failed, @ignored ignored) - continuing with '@name'";
-    }
-    $this->message->display(\Drupal::translation()->formatPlural($processed,
-      $singular_message, $plural_message,
-        [
-          '@numitems' => $processed,
-          '@created' => $this->getCreatedCount(),
-          '@updated' => $this->getUpdatedCount(),
-          '@failures' => $this->getFailedCount(),
-          '@ignored' => $this->getIgnoredCount(),
-          '@name' => $this->migration->id(),
-        ]
-    ));
+  protected function progressMessageEmit($done = TRUE) {
+    $this->message->display($this->progressMessage($done));
   }
 
   /**
@@ -382,7 +394,7 @@ class MigrateExecutable extends MigrateExecutableBase {
       }
     }
     if ($this->feedback && ($this->counter) && $this->counter % $this->feedback == 0) {
-      $this->progressMessage(FALSE);
+      $this->progressMessageEmit(FALSE);
       $this->resetCounters();
     }
     $this->counter++;
