Schema migrations are the part of shipping software that nobody wants to think about until they break. For ops teams who've been burned, the hardest sell from any tool is: do you get to run migrations the way you already run them in production?
On AlgorithmShift the answer is yes — you pick the mode per environment. Here's what that looks like.
The three modes
Every environment — dev, staging, prod, or whatever naming your team uses — carries a migration_mode setting. It controls exactly what the platform is allowed to do against that environment's database:
One platform, three contracts with your database. You don't pick once; you pick per environment. Dev can be fully automated while prod stays locked down behind the same pipeline it had last year.
Managed mode
The default for dev. You click Run migrations, the platform:
- Figures out what's pending for that environment (platform-owned ledger, not a table inside your DB).
- Applies each pending migration, in order, in a transaction.
- Records the apply — who, when, the content hash at apply time.
Dev teams use this because it's fast — a PM approving a schema change can see the schema live in seconds. A PM never has to open pgAdmin.
What you get:
- No thinking about migration order.
- Crash-safe retry (every migration the agent produces is idempotent, so a half-applied run replays cleanly).
- Per-user attribution on every applied change.
When not to use: anywhere you don't want us holding credentials. Which is usually everywhere that isn't dev.
Export-only mode
The default for environments named staging or prod. We default you into this so the platform cannot accidentally touch a customer-facing database.
The flow:
- You click Export SQL bundle on the environment — or better, on a release.
- Platform concatenates the relevant migrations into a single
.sqlfile. - You download it (or, in a future update, we PR it into your infra repo).
- Your pipeline — Flyway, Liquibase, plain
psql, whatever you already use — applies it. - You come back and click Mark applied so the platform knows where each migration landed.
The platform never authenticates to that database. It never reads your ledger. All it does is produce the SQL and remember what you told it was applied where.
What you get:
- Zero trust changes. Your prod DB still only accepts writes from your existing pipeline.
- Release-scoped bundles — promoting a release means exactly those migrations, no cherry-picking from a wiki.
- A cross-environment view of what's applied where, without any special access.
When not to use: a hobby project where you don't want the ceremony. Use managed for that.
Reconcile-only mode
The middle ground. Common for staging: your team already runs Flyway or Liquibase there, and the platform shouldn't write anything — but it's nice if the platform knows which migrations are applied.
Give us a read-only credential. We'll look for a migration ledger in a few known shapes:
flyway_schema_history(Flyway)DATABASECHANGELOG(Liquibase)schema_migrations(Rails / Django / Phoenix)- our own
_migrationsshape, if you ran a past export through it
Whichever we find, we read it periodically and reflect the state in the platform UI. No writes. Ever.
What you get:
- Cross-environment visibility without handing over write access.
- Drift detection — we alert when a migration you have in dev isn't yet in staging.
- One place to see your full release manifest, even when the pipeline applying it is outside our tool.
How migrations themselves are safe to replay
Whichever mode you pick, the migration files the platform generates are idempotent. CREATE TABLE IF NOT EXISTS, ALTER TABLE ... ADD COLUMN IF NOT EXISTS, and so on. The schema agent is prompted explicitly to never emit a migration that fails on replay.
This matters most in managed mode, where a crash mid-run should be able to pick up from where it left off on the next click. It also matters in export mode: if your CI accidentally double-applies a bundle, it's a no-op, not a data incident.
Drift detection
Every time a migration is applied (or marked applied, or reconciled), we record the hash of the file at that moment. If the file later changes on disk, you see a drift indicator in the UI.
Drift is usually one of two things:
- Someone edited the migration after it was applied. Don't do that — produce a follow-up migration instead.
- Someone reverted the migration file manually. In which case the ledger knows the env has a state the file no longer represents.
Either way, you see it before it becomes a story.
Releases carry their migrations
A release bundles its shipped code, its stories, its notes, and its migrations. When you promote a release from dev to staging, you're not cherry-picking from a list — you export exactly the migrations attached to that release, as one coherent bundle, versioned with it.
The release detail page has a Schema tab with the migration manifest for that release, a per-environment apply matrix, and a one-click export button.
What this means for you
Most teams end up with managed in dev, export_only in staging and prod, and never change it. If yours has a different shape — a tightly-controlled reference environment, for instance — set that one to reconcile_only and move on.
The goal was never to make you adopt a new migration pipeline. It was to let the platform do the repetitive, error-prone schema-authoring work while your existing ops flow stays whatever you want it to be.
If you've got a specific setup you want to talk through, book a walkthrough. Migration ceremony is the first thing engineering teams ask about, and we've got answers that aren't "trust us."