DEV Community

Cover image for 7 Things I Wish migrate-mongo Had After a Few Production Incidents
Santosh Gupta
Santosh Gupta

Posted on

7 Things I Wish migrate-mongo Had After a Few Production Incidents

I've used migrate-mongo for years.

It's simple, reliable, and honestly a good choice for many projects.

But as our application grew, I started running into the same problems repeatedly: rollbacks, concurrent deployments, migration drift, and debugging what actually happened months ago.

Eventually, I stopped working around those problems and built mongo-migrate-kit.

Here are the seven things I wanted.


1. Roll back a specific migration

This was the one that frustrated me the most.

Imagine three migrations went out:

migration A  ✓
migration B  ✗
migration C  ✓
Enter fullscreen mode Exit fullscreen mode

I only want to undo B.

With migrate-mongo, rollback is based on the latest migration. There's no simple way to say "undo this particular migration."

So you either roll back more than you want or start doing things manually.

I've done the latter in production. Not fun.

With mongo-migrate-kit:

mmk down 20260101-the-broken-one.js
Enter fullscreen mode Exit fullscreen mode

Or if you actually want to roll back a batch:

mmk down --batch 3
Enter fullscreen mode Exit fullscreen mode

2. Dry run before touching production

Before running migrations in production, I want to know:

  • Which migrations are pending?
  • What order will they run in?
  • What is about to happen?

That's why mmk has dry-run support:

mmk dry-run up
mmk dry-run down
Enter fullscreen mode Exit fullscreen mode

It shows the plan without changing the database.

I run this before production migrations now. It's a small thing, but it removes a lot of uncertainty.


3. Two deployments shouldn't run migrations simultaneously

We once had two deployments trigger migrations almost at the same time.

Both processes thought they were in charge.

That's not a situation you want with database migrations.

mongo-migrate-kit uses a database-backed atomic lock:

Deploy A → acquire lock → run migrations → release

Deploy B → lock exists → stop
Enter fullscreen mode Exit fullscreen mode

The lock has a TTL, so a crashed process doesn't leave the database permanently locked.

And if you don't need locking locally:

mmk up --no-lock
Enter fullscreen mode Exit fullscreen mode

4. Know when an old migration was modified

This one is easy to overlook.

A migration runs in production.

Someone later edits the file.

The filename is still the same, so the migration history says everything is fine.

But the code that exists in Git is no longer the code that ran in production.

mongo-migrate-kit stores a SHA-256 checksum for every migration.

If the file changes, you can see the drift.

And with:

mmk up --strict
Enter fullscreen mode Exit fullscreen mode

the migration can fail instead of silently continuing.

If the change was intentional:

mmk up 20260101-add-index.js --force
Enter fullscreen mode Exit fullscreen mode

Migration files are history. I think they should be treated that way.


5. Redo shouldn't require two commands

During development, my migration loop was basically:

migrate down
migrate up
Enter fullscreen mode Exit fullscreen mode

Over and over.

So I added:

mmk redo
Enter fullscreen mode Exit fullscreen mode

Or:

mmk redo 20260101-add-index.js
Enter fullscreen mode Exit fullscreen mode

Small feature.

Probably the command I use most while writing migrations.


6. Rollbacks shouldn't erase history

A rollback is still an event.

If a migration ran on Monday and was reverted on Tuesday, I want that information to remain in the database.

Instead of deleting the migration record, mmk marks it as:

applied → reverted
Enter fullscreen mode Exit fullscreen mode

The history keeps information such as:

batch
status
appliedAt
revertedAt
duration
checksum
environment
executedBy
Enter fullscreen mode Exit fullscreen mode

Months later, you can still answer:

"Did this migration actually run in production?"

That's important when debugging old incidents.


7. TypeScript shouldn't need a separate setup

If your application is TypeScript, migration files should be TypeScript too.

With mongo-migrate-kit:

import type { MigrationContext } from 'mongo-migrate-kit';

export async function up({ db }: MigrationContext): Promise<void> {
  await db.collection('users').createIndex(
    { email: 1 },
    { unique: true }
  );
}

export async function down({ db }: MigrationContext): Promise<void> {
  await db.collection('users').dropIndex('email_1');
}
Enter fullscreen mode Exit fullscreen mode

No extra ts-node setup or build step.

JavaScript works too, including ESM and CommonJS.


Should you switch from migrate-mongo?

Maybe not.

If you have a small project and a handful of migrations, migrate-mongo is probably perfectly fine.

I used it for years.

These problems only started becoming painful when we had more migrations, more developers, more environments, and CI/CD deployments running against production.

That's when migration tooling stops being just:

"Run these files in order."

It becomes infrastructure.

And infrastructure needs things like locking, audit history, drift detection, and safe rollbacks.


Already using migrate-mongo?

You don't need to rewrite your existing migrations.

mongo-migrate-kit can import your existing history:

npm install mongo-migrate-kit mongodb

mmk import --dry-run
mmk import
mmk up
Enter fullscreen mode Exit fullscreen mode

The old changelog isn't modified.

That's the reason I built this tool.

Not because migrate-mongo is bad.

Because after enough production incidents, I knew exactly which features I wanted in my migration workflow.

mongo-migrate-kit is open source.

If you've had a migration go wrong in production, I'd genuinely like to hear what happened. Those stories are usually where the best tooling ideas come from.

Top comments (0)