Skip to content
Published on

DB Migration Tools 2026 — Atlas / Flyway / Liquibase / Bytebase / pgroll / Squawk / gh-ost Deep Dive

Authors

Prologue — "One migration line, 5 minutes of downtime"

A 2026 team postmortem.

23:14, routine deploy. ALTER TABLE orders ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'. 23:14:08, primary acquires lock. 23:14:09, all queries start queueing. 23:14:42, backend exhausts connection pool, 5xx begins. 23:19:31, migration ends. 23:19:34, queue drains, normal. 5 minutes 22 seconds of downtime. The table had 400 million rows.

This is still a common 2026 scene. There is no shortage of tools — Flyway, Liquibase, Atlas, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma Migrate, Drizzle Kit. But if the tool does not tell you which migrations are safe, the 5-minute outage repeats.

This post maps the 2026 DB migration landscape. Who is good at what, who is not, and what your team should pick.


Chapter 1 · The 2026 DB Migration Map — Declarative vs Imperative vs Online

The big picture first. Every migration tool sits on one of three axes.

Axis 1: declarative vs imperative

  • Imperative: humans write ALTER TABLE, CREATE INDEX SQL scripts, and the tool tracks which scripts ran in what order. Flyway, Liquibase, dbmate, golang-migrate, Alembic, Sqitch, Diesel, Prisma Migrate (default).
  • Declarative: humans write "desired schema state" only, and the tool diffs it against the current DB to generate ALTER statements. Atlas, Drizzle Kit, Prisma Migrate (prototype / db push), Bytebase GitOps mode.

Declarative does for DB schemas what Terraform did for infrastructure. Pro: the schema reads in one place as code. Con: there is no guarantee the generated ALTER is safe (rename can be decomposed into drop+add).

Axis 2: online vs offline

  • Offline: during migration, tables are locked or queries are blocked. The default for most traditional tools.
  • Online: schema changes happen while live traffic flows. gh-ost, pt-osc, Reshape, pgroll. They use shadow tables + trigger or logical replication to copy slowly, then cut over.

For small tables (under a few hundred thousand rows), offline is simple. For large tables (hundreds of millions of rows, high traffic), online is mandatory.

Axis 3: forward-only vs reversible

  • Reversible: every up migration must pair with a down. Flyway, Alembic, golang-migrate recommend this. Roll back on incident.
  • Forward-only: rollbacks are done by adding new migrations. Sqitch and the recommended Atlas mode. Reasoning: "almost no down migration can reverse a 400-million-row INSERT in 5 minutes — it is a fantasy."

A 2026 senior consensus: forward-only plus a sequence of short, safe changes is more realistic than reversible. Down migrations are meaningful only up to "drop the empty column we just added".

One-page summary

ToolModelOnline?Language / targetRecommended for
Atlasdeclarativedirect no, integrations yesmulti-DBdeclarative schema plus CI
Flyway 11imperativenoJava-friendlyenterprise standard
Liquibase 4.30imperative plus metanoJava, XML/YAMLlarge org, multi-DB
Sqitchimperative plus DAGnoDB neutraldependency-explicit
Bytebase 3both plus workflowintegration yesmulti-DBDB DevOps, review
Squawklintern/aPostgresCI gate
pgrolldeclarative plus expand/contractyesPostgreszero-downtime Postgres
gh-ostonline changeyesMySQLlarge-scale MySQL
pt-osconline changeyesMySQLclassic MySQL
Reshapeonline changeyesPostgresRust, zero-downtime
dbmateimperativenomulti-DB, Go CLIlightweight CLI
golang-migrateimperativenoGo, multi-DBGo default
Dieselimperative plus ORMnoRustRust-friendly
Alembicimperative plus autogennoPython, SQLAlchemyPython default
Prisma MigratebothnoTypeScriptNode-friendly
Drizzle Kitdeclarative plus imperativenoTypeScriptTS, lightweight

That is the 2026 map. Now into each cell.


Chapter 2 · Expand-Contract Pattern + Common Foot-Guns

Before picking a tool, learn the pattern. Even with a great tool, a bad pattern still locks tables.

Expand-Contract (Parallel Change)

The core pattern for zero-downtime migrations. Split into three steps.

  1. Expand: add only, so the old and new schema coexist. No drops.
  2. Migrate: code writes and reads against the new schema. A backfill copies old data into the new shape.
  3. Contract: once all code is confirmed to use the new schema only, drop the old.

Example — rename a column (email to email_address).

  • A straight rename is dangerous: code currently deploying that reads the old column breaks instantly.
  • Expand-contract: add email_address as nullable, code writes both columns, backfill old data, code reads new column, drop old column.

pgroll, Reshape, gh-ost automate this pattern as their default model. Other tools require humans to split into three migrations.

Common foot-guns

1) Adding NOT NULL on a big table

In Postgres 11+, ALTER TABLE ... ADD COLUMN x INT NOT NULL DEFAULT 0 only flips metadata (fast). But ALTER TABLE ... ALTER COLUMN x SET NOT NULL scans the whole table while holding ACCESS EXCLUSIVE. At 400M rows, that is minutes of lock.

  • Safe: ALTER TABLE ... ADD CONSTRAINT x_not_null CHECK (x IS NOT NULL) NOT VALID, then VALIDATE CONSTRAINT x_not_null, then in a later deploy SET NOT NULL. NOT VALID takes no lock.
  • MySQL: similar pattern plus gh-ost / pt-osc.

2) Column rename

  • A plain ALTER TABLE ... RENAME COLUMN is usually metadata-only, but deploying code that references the old name breaks immediately.
  • Safe: expand-contract.

3) Type change (INT to BIGINT)

  • Postgres: usually a full table rewrite. Large tables, lock for minutes.
  • Safe: add new BIGINT column, backfill, switch code, drop old column.

4) Adding an index

  • Postgres: CREATE INDEX CONCURRENTLY takes no lock. If your migration tool wraps it in a transaction, it fails — it must run outside a transaction.
  • MySQL: small tables are instant. Big tables need gh-ost / pt-osc.

5) Changing default value (Postgres before 11 / some types)

  • Pre-11: adding a default rewrote the whole table. 11+, metadata-only.
  • Safe: upgrade to 11+. Or add without default and backfill.

6) Adding a foreign key

  • The lock is short but covers both tables. Under heavy traffic, timeout risk.
  • Safe: NOT VALID to add, then a separate transaction for VALIDATE CONSTRAINT.

7) Large DELETE / UPDATE

  • Updating millions of rows inside a single transaction: WAL explosion, replica lag, lock held.
  • Safe: batch (e.g. 10K rows at a time with sleeps). Migration tools do not do this for you — it is a separate backfill job.

The takeaway: a migration tool is just "something that runs scripts in order"; it does not guarantee safety. Safety comes from the pattern, a linter like Squawk, and review.


Chapter 3 · Atlas (Ariga) — The Declarative Standard

Atlas (atlasgo.io) started in 2022 and grew its share quickly in 2024-2025. It does for DB schemas what HashiCorp Terraform did for infrastructure.

Model

You write the desired schema in schema.hcl (or SQL / JSON). Atlas diffs it against the live DB and generates ALTER statements.

schema "public" {}

table "users" {
  schema = schema.public
  column "id" {
    type = bigint
    null = false
  }
  column "email" {
    type = varchar(255)
    null = false
  }
  primary_key { columns = [column.id] }
  index "idx_users_email" {
    columns = [column.email]
    unique  = true
  }
}

The CLI has two modes.

  • Declarative: atlas schema apply --to file://schema.hcl --url $DB_URL — diff and apply directly.
  • Versioned: atlas migrate diff add_user_email — write the diff out as a SQL file and later apply with atlas migrate apply. Recommended for production.

Strengths

  • DB neutral: Postgres, MySQL, SQLite, SQL Server, MariaDB, ClickHouse, MS SQL, Redshift, and more.
  • CI integration: atlas migrate lint automatically detects risky changes (NOT NULL adds, DROP COLUMN, missing indexes). GitHub Actions and GitLab CI integrations.
  • ORM integration: extract schemas from GORM, Ent, Hibernate, Prisma, Sequelize and manage them in Atlas.
  • Schema visualization: atlas schema inspect plus atlas schema fmt plus ERD generation.

Weaknesses

  • The declarative model tends to handle column rename as drop+add. Atlas can be hinted with --diff-policy, but not automatically.
  • Zero-downtime (expand-contract) is not directly supported — versioned mode requires humans to split.
  • The gap between free and paid (Atlas Cloud, Pro) is large: some lint rules, schema monitoring, and multi-tenant are Pro-only.

Where to use

  • New Go backends.
  • Teams comfortable with IaC like Terraform.
  • Multi-DB environments where you want one tool.

In 2026 Atlas is the de facto standard of the declarative camp.


Chapter 4 · Flyway 11 — The Java Classic

Flyway has existed since 2010 and is the standard in the JBoss / Spring Boot ecosystem. Redgate acquired it in 2024, and 11.x is focused on module cleanup and new database adapters.

Model

V{version}__{description}.sql naming convention. Plain imperative.

db/migration/
  V1__create_users.sql
  V2__add_email_index.sql
  V3__add_orders.sql
  R__create_view_active_users.sql   # Repeatable
  U2__rollback_email_index.sql       # Undo (Teams)

A flyway_schema_history table in the DB tracks which Vs have run. flyway migrate applies only the unapplied ones in order.

Strengths

  • Simple: if you know SQL, you are done. A new teammate gets it in 30 minutes.
  • Spring Boot autoconfig: one line — spring-boot-starter-flyway.
  • Wide DB coverage: Postgres, MySQL, Oracle, SQL Server, DB2, Sybase, MongoDB (Pro), Snowflake, BigQuery, 50+ in total.
  • Callback hooks: beforeMigrate, afterMigrate, beforeEachMigrate, lifecycle hooks.
  • Baseline: mark an existing DB as the baseline when introducing Flyway later.

Weaknesses

  • Rollback is paid (Teams / Enterprise): OSS is forward-only. Undo migrations are Teams-only.
  • No online schema change: large tables need gh-ost / pt-osc / pgroll alongside.
  • No declarative mode: 11.x added some auto-generate, but not at Atlas / Drizzle level.
  • Weak linting: detecting risky patterns is delegated to external tools like Squawk.

Where to use

  • Spring Boot / Java / Kotlin backends.
  • Enterprise DBs like Oracle and DB2.
  • Teams that want "SQL as written, fewer abstractions".

In 2026 Flyway remains the default in the JVM camp. But fewer new projects use Flyway alone — combinations with Atlas / Squawk are common.


Chapter 5 · Liquibase 4.30 — XML / YAML Friendly

Liquibase is the Flyway rival. From 2006, 4.30 (2025-2026) focuses on changelog formats, DB support, and UX.

Model

List changeSets in a changelog. The format can be XML, YAML, JSON, or SQL.

databaseChangeLog:
  - changeSet:
      id: 1
      author: alice
      changes:
        - createTable:
            tableName: users
            columns:
              - column:
                  name: id
                  type: BIGINT
                  autoIncrement: true
                  constraints: { primaryKey: true, nullable: false }
              - column:
                  name: email
                  type: VARCHAR(255)
                  constraints: { nullable: false, unique: true }
      rollback:
        - dropTable: { tableName: users }

Strengths

  • Strong DB abstraction: the same changeSet translates to the appropriate SQL on Postgres, MySQL, Oracle. Useful for cross-DB projects.
  • First-class rollback in OSS: each change can declare its rollback explicitly.
  • Policy checks (Pro): automatic blocking of risky patterns.
  • GUI tools (Liquibase Hub, DBeaver integration).
  • Many change types: addColumn, createIndex, addForeignKey, mergeColumns, 30+ abstract changes.

Weaknesses

  • XML / YAML verbosity: one SQL line becomes ten YAML lines.
  • The cost of abstraction: the SQL generated by abstract changes is not always optimal. On large tables you fall back to raw SQL.
  • Weak declarative: Liquibase is imperative. If you want declarative, look elsewhere.
  • Large OSS-vs-Pro gap: policy check, parts of rollback, and Hub are Pro.

Flyway vs Liquibase in one line

  • Flyway: if you like SQL.
  • Liquibase: if you need cross-DB, abstraction, explicit rollback.

In 2026 they are roughly tied, but new projects clearly trend toward Atlas.


Chapter 6 · Sqitch — The Old-School Dependency Camp

Sqitch (sqitch.org) was built by David Wheeler in 2012 as a "Git-style migration tool". It tracks migrations by a dependency graph, not by version numbers.

Model

Each change has a name. sqitch add appchanges creates three files.

deploy/appchanges.sql    -- apply SQL
revert/appchanges.sql    -- rollback SQL
verify/appchanges.sql    -- verification SELECT

Dependencies live in sqitch.plan.

add_users 2026-05-16T12:00:00Z alice
add_orders [add_users] 2026-05-16T13:00:00Z alice  -- depends on add_users

sqitch deploy applies in dependency-satisfying order. sqitch verify runs the verify SQL to confirm it actually landed.

Strengths

  • Explicit dependencies: once you cross 100 migrations, the graph really pays off.
  • First-class verify: separates "the migration was applied" from "it actually works".
  • DB neutral: Postgres, MySQL, Oracle, SQLite, Snowflake, Firebird, Vertica, Exasol.
  • VCS-friendly: fewer migration conflicts across branches (because dependencies, not numbers).

Weaknesses

  • Learning curve: many commands (deploy, revert, verify, bundle, tag, rework) and a different mental model.
  • Small community: roughly a tenth of Flyway / Liquibase in GitHub stars and StackOverflow answers.
  • Weak CI integration and GUI: everything is CLI.
  • Perl dependency: install is a bit more involved than other tools (Docker is the usual workaround).

Where to use

  • Monolith DBs with more than 1000 migrations.
  • Multi-branch environments where "version number conflicts" are frequent.
  • Teams that like the dependency-graph + verify model.

In 2026 Sqitch is a niche but loyal tool. Some PostgreSQL core contributors still prefer it.


Chapter 7 · Bytebase 3 — DB DevOps + Review

Bytebase (bytebase.com) started in 2021 and jumped to 3.x in 2025. Concept: "GitLab / GitHub for DBs".

Model

Web UI plus GitOps. You raise a migration SQL as a PR, and Bytebase:

  1. Auto-lints (SQL Review, a Squawk-style ruleset).
  2. Approval workflow (DBA reviews).
  3. Per-stage auto-deploy (dev → staging → prod).
  4. Schema drift detection (alerts if a change Bytebase did not know about lands in prod).
  5. Data masking, access control (who can see which columns).

Strengths

  • First-class human workflow: other tools stop at CLI / CI; Bytebase puts the human-review UI front and center.
  • Multi-DB, multi-environment: 50+ DBs, dev/staging/prod lifecycle.
  • Schema editor (GUI): non-developers can propose migrations.
  • Data masking: hide PII columns from developers.
  • Self-hosted OSS: free to self-host (license gates some features).

Weaknesses

  • Infra cost: Bytebase itself is a server plus DB. Overkill for small teams.
  • Vendor lock-in: switching away from GitOps mode is hard.
  • Bytebase delegates the actual migration: it does not run ALTERs directly — it invokes gh-ost / pt-osc / Flyway / Liquibase. It is closer to an orchestrator.

Where to use

  • Environments with a DBA team where developers should not touch prod DB directly.
  • Large orgs running 50+ DB instances.
  • Teams that want "PR + approval" workflow applied to DBs.

In 2026 Bytebase is the de facto leader of DB DevOps. Serverless DBs like PlanetScale and Neon have nice native UIs, but Bytebase wins on on-prem / multi-cloud.


Chapter 8 · Squawk — A Postgres Migration Linter

Squawk (squawkhq.com) is a small OSS tool by Steve Dignam from 2020. It reads migration SQL and flags risky patterns. Not a migration tool — a linter.

Model

squawk migration.sql

When it finds something risky, you get an error or warning.

migration.sql:1:1: warning: adding-not-nullable-field
  ALTER TABLE "user" ADD COLUMN "email" TEXT NOT NULL
  Adding a NOT NULL field requires a table rewrite ...
  Use ADD COLUMN ... DEFAULT NULL, backfill, then ALTER ... SET NOT NULL.

Detection rules (Postgres-centric, 30+)

  • adding-not-nullable-field — adding NOT NULL → lock.
  • changing-column-type — type change → rewrite.
  • disallowed-unique-constraint — adding UNIQUE → full scan, lock.
  • prefer-text-field — TEXT instead of VARCHAR(n).
  • require-concurrent-index-creationCREATE INDEX without CONCURRENTLY → lock.
  • transaction-nestingCREATE INDEX CONCURRENTLY inside a transaction → fails.
  • renaming-column — rename breaks deploying code immediately.
  • renaming-table — same.
  • ban-drop-column — DROP COLUMN belongs in expand-contract.
  • constraint-missing-not-validADD CONSTRAINT without NOT VALID → full scan.

Strengths

  • One line in CI: squawk **/V*.sql — automatic gate on every migration.
  • Fast: written in Rust, milliseconds per file.
  • GitHub Actions comments: posts warnings as PR comments.
  • Migration-tool neutral: lints SQL produced by Flyway, Liquibase, Atlas, dbmate, anything.

Weaknesses

  • Postgres only: there are almost no MySQL rules.
  • False positives: it does not know "this table is small so NOT NULL is fine". Disable per rule to work around.
  • No data-safety checks: it sees the lock, but not "is this column actually unused?"

Where to use

  • Add to the CI of every team using Postgres (free, one line).
  • Practically required if you have any large tables.

A 2026 senior consensus: if you run Postgres, Squawk is the default. It goes into CI regardless of which migration tool you chose.


Chapter 9 · pgroll (Xata) — Zero-Downtime Postgres Migrations

pgroll (github.com/xataio/pgroll) was open-sourced by Xata in 2023-2024. The ambition: the tool itself automates expand-contract.

Model

Define migrations in JSON. pgroll automatically creates views + triggers so the old and new schema are exposed at the same time.

{
  "name": "rename_email_column",
  "operations": [
    {
      "rename_column": {
        "table": "users",
        "column": "email",
        "to": "email_address"
      }
    }
  ]
}

pgroll start to begin → two versions coexist (exposed via views) → pgroll complete to drop the old column. You can pgroll rollback mid-way.

How it works

  1. pgroll start adds the new column/table/index and creates views.
  2. The old view = old schema, the new view = new schema. Code chooses which view via SET search_path.
  3. Triggers sync data both ways (writes to the old column also update the new, and vice versa).
  4. A backfill runs in the background to copy old data into the new column.
  5. Once all traffic is on the new view, pgroll complete drops the old column.

Strengths

  • Truly zero-downtime: rename, type change, NOT NULL adds all work while traffic flows.
  • Safe rollback: before cutover the old view is still alive, so revert is instant.
  • Declarative plus expand-contract: you do not have to split into three migrations.
  • Native Postgres 14+: uses triggers, views, and logical decoding. No external deps.

Weaknesses

  • Postgres only.
  • Trigger overhead: bidirectional sync triggers add write load.
  • Userbase is still small: in 2026 production stories are limited to the likes of Toss, Notion, Xata. More cases needed.
  • Not every change is supported: partitions and some complex constraints are not there yet.

Where to use

  • Postgres-backed services that genuinely need zero downtime (24/7 payments, messaging).
  • Fast-evolving schemas where column renames and type changes are frequent.

In 2026 pgroll is the best bet for zero-downtime Postgres migrations. Maturity is still behind gh-ost (MySQL) though.


Chapter 10 · gh-ost / pt-online-schema-change / Reshape — Online Schema Change

Large tables plus live traffic = online schema change. Three tools.

gh-ost (GitHub, MySQL)

GitHub built and open-sourced this in 2016. Trigger-free, binlog-based.

How it works:

  1. Create a shadow table with the new schema.
  2. Copy rows from the old table to the shadow in chunks.
  3. Read the binlog and apply in-flight changes (INSERT / UPDATE / DELETE) to the shadow.
  4. Cut over: a short lock swaps old and shadow atomically.
gh-ost \
  --user="root" --password="..." \
  --host=primary.db \
  --database="app" --table="orders" \
  --alter="ADD COLUMN status_v2 VARCHAR(32) NOT NULL DEFAULT 'pending'" \
  --execute
  • Strengths: no triggers means less load on the old table. Great throttle (watches replica lag and slows automatically). Interactive (the CLI can pause / cancel mid-run).
  • Weaknesses: MySQL / MariaDB only. Needs binlog read access. Tables with foreign keys are tricky.

pt-online-schema-change (Percona, MySQL)

Part of Percona Toolkit. Trigger-based.

How it works:

  1. Create the shadow table and install INSERT / UPDATE / DELETE triggers on the old.
  2. Copy old to shadow in chunks.
  3. Cut over: two RENAME TABLEs to swap.
  • Strengths: old and stable. Handles some cases gh-ost cannot (mainly foreign keys).
  • Weaknesses: triggers raise write load on the old. Weaker throttling than gh-ost.

Reshape (Postgres, Rust)

Built by Fabian Lindfors in 2022. Postgres expand-contract.

How it works: similar to pgroll. Define migrations in TOML, not SQL, then views + triggers expose old and new in parallel.

[[actions]]
type = "add_column"
table = "users"
column = "email_address"
data_type = "text"
nullable = true
  • Strengths: fast Rust, clean API.
  • Weaknesses: maintainer activity has reportedly slowed since 2024. Production use in 2026 is small. pgroll has largely overtaken it.

Three-way comparison

gh-ostpt-oscReshapepgroll
DBMySQLMySQLPostgresPostgres
Triggersnoyesyesyes
Binlogyesnonono
Activity (2026)mediumlowlowhigh
Recommendedlarge MySQLgh-ost edge cases(avoid)Postgres

The 2026 consensus: MySQL: gh-ost default, pt-osc fallback. Postgres: pgroll as the rising standard.


Chapter 11 · dbmate / golang-migrate / Diesel / Alembic — Language Camps

Each language ecosystem has its default.

dbmate (Go, multi-DB)

amacneil/dbmate. A single-file Go binary with a clean CLI.

dbmate new add_users
# creates db/migrations/20260516120000_add_users.sql

Each file has up / down sections.

-- migrate:up
CREATE TABLE users (id BIGINT PRIMARY KEY, email TEXT NOT NULL);

-- migrate:down
DROP TABLE users;
  • Strengths: Postgres, MySQL, SQLite, ClickHouse, BigQuery. Zero deps. Shell-friendly. Docker-friendly.
  • Weaknesses: few features (no lint, no schema drift, no rollback workflow). That simplicity is the charm.

golang-migrate (Go, multi-DB)

golang-migrate/migrate. CLI plus Go library.

migrations/
  000001_create_users.up.sql
  000001_create_users.down.sql
  • Strengths: callable as a Go library from your service. CockroachDB, Spanner, MongoDB adapters.
  • Weaknesses: high GitHub stars but maintainer bandwidth has shrunk. Issue backlog growing. Some adapters in maintenance-only mode.

dbmate vs golang-migrate — in 2024-2025 many teams switched to dbmate. For new Go projects dbmate is the lighter default (or Atlas).

Diesel migrations (Rust)

Part of the Diesel ORM. diesel migration generate add_users produces up.sql / down.sql.

  • Strengths: tight integration with Diesel — generates schema.rs, compile-time type checks.
  • Weaknesses: Diesel itself is a heavy ORM. Many teams prefer SQLx / sqlx::migrate.

Alembic (Python, SQLAlchemy)

The migration tool for SQLAlchemy. Migrations are Python code.

def upgrade():
    op.add_column('users', sa.Column('email', sa.String(255), nullable=False))

def downgrade():
    op.drop_column('users', 'email')
  • Strengths: autogenerate from SQLAlchemy models — change the model and Alembic diffs it into a migration. Python lets you write conditional logic like if dialect == 'postgresql':.
  • Weaknesses: autogenerate misreads column renames as drop+add. Python dependency footprint is heavy. Bad fit if you do not use SQLAlchemy.

In 2026, Django still uses Django migrations, while FastAPI / SQLAlchemy still defaults to Alembic.


Chapter 12 · Prisma Migrate / Drizzle Kit — The TypeScript Camp

Node / TypeScript is fragmented. Two leaders.

Prisma Migrate

Part of the Prisma ORM. Write models in schema.prisma, then prisma migrate dev generates and applies migrations.

model User {
  id    BigInt  @id @default(autoincrement())
  email String  @unique
}
  • Strengths: schema → migration → client types in one motion. Best-in-class DX.
  • Weaknesses: heavy Rust engine. Weak raw-SQL control. Does not guarantee safe migrations on large tables (renames become drop+add). Conflicts with pgbouncer transaction mode.

Drizzle Kit

Part of the Drizzle ORM. Schema defined in TypeScript code, then drizzle-kit generate produces a SQL diff.

export const users = pgTable("users", {
  id: bigserial("id").primaryKey(),
  email: varchar("email", { length: 255 }).notNull().unique(),
});
  • Strengths: lightweight (tiny runtime deps). Close to raw SQL — humans read and tweak the generated SQL. Edge-runtime friendly (Cloudflare Workers).
  • Weaknesses: ecosystem is still smaller than Prisma in 2026. Some advanced features (relations, complex types) lag.

The 2026 TS consensus:

  • DX-first, full-stack Node → Prisma.
  • Edge / serverless / raw SQL control → Drizzle.
  • Large scale / zero-downtime → both are insufficient; pair with Atlas / pgroll / gh-ost.

Chapter 13 · Who Picks What — A Decision Tree

SituationRecommendation
Single app, Postgres, low trafficdbmate or golang-migrate plus Squawk
Single app, MySQL, low trafficFlyway or dbmate
Java / Spring BootFlyway (or Liquibase) plus Squawk
Node / TypeScript, full-stackPrisma plus Atlas at scale
Node / TypeScript, edgeDrizzle Kit plus Squawk
Python / DjangoDjango migrations
Python / FastAPIAlembic
RustDiesel or SQLx plus dbmate
Go, new projectAtlas (declarative) or dbmate (simple)
Microservices 50+Bytebase plus each service's tool
Large Postgres, zero downtime requiredpgroll plus Squawk plus Atlas
Large MySQL, zero downtime requiredgh-ost plus Flyway plus a Squawk-MySQL alternative
DBA team present, review workflowBytebase-centric
Cross-DB (Postgres + MySQL + Oracle)Liquibase or Bytebase
1000+ accumulated migrationsSqitch (dependency graph)

Common antipatterns

  • "Prisma Migrate solves everything to production": fine on small tables, but locks at scale. Gate with Squawk.
  • "Liquibase abstract changes always do the right thing": often generates suboptimal SQL cross-DB. Drop to raw SQL and verify.
  • "Always write down migrations": do you really believe a 400M-row INSERT down can run in 5 minutes? Forward-only is more realistic.
  • "The migration tool guarantees safety": no tool adds NOT NULL safely on its own. Safety = expand-contract + lint + review.
  • "Run gh-ost once and you are zero-downtime": misconfigured throttle blows up replica lag. If the cutover lock exceeds 5 seconds, you fail.

Chapter 14 · Korea / Japan Cases — Toss / Kakao / Mercari

Toss

At Toss SLASH 2023, Toss disclosed broad use of gh-ost on MySQL. The payments domain is 24/7, so any lock is revenue loss. They tune chunk size and throttle per domain with gh-ost.

In 2025-2026 some new Postgres services at Toss are reportedly evaluating pgroll. Squawk is a mandatory CI step on every new migration PR.

Kakao

Kakao's messenger / pay / bank each take a different DB strategy. Kakao Bank runs on Oracle, so Liquibase plus a custom workflow. Some new services in Kakao Pay use Flyway plus gh-ost (MySQL). Kakao Messenger runs on a proprietary distributed storage, so general migration tools do not apply.

In 2024 some Kakao teams reportedly adopted Atlas to unify their Go backends, as discussed at internal conferences.

Mercari (Japan)

Mercari runs 100+ microservices. Each service picks its own tool, but lint (Squawk plus internal rules) is the shared CI step. Many services use Spanner, so the Spanner CLI plus a homegrown migration framework dominates there.

The Mercari Engineering blog frequently writes about "Schema migration as code" — an internal implementation of a declarative model similar to Atlas / Bytebase.

LY Corporation (LINE + Yahoo Japan)

After the 2023 merger, LINE's core backend DB migrations are standardizing on Flyway. Some new Go services use Atlas. An internal sql-lint tool acts as a Squawk-MySQL substitute with custom rules.

Common patterns:

  • Large tables always go through gh-ost (MySQL) / pgroll (Postgres).
  • Lint gates in CI are default.
  • DBA review is the prod-deploy gate (whether Bytebase or homegrown).

That is the 2026 flow at Korean / Japanese big tech.


Conclusion — Many tools, few patterns

The whole post in one page:

  1. Tools are plentiful. Atlas, Flyway, Liquibase, Sqitch, Bytebase, pgroll, Squawk, gh-ost, pt-osc, Reshape, dbmate, golang-migrate, Diesel, Alembic, Prisma, Drizzle. No need for another.
  2. Start your selection with language and team fit. Java → Flyway, Python → Alembic, Go → Atlas / dbmate, TS → Prisma / Drizzle.
  3. If you need zero-downtime, pattern decides the tool. Expand-contract → pgroll (Postgres) / gh-ost (MySQL).
  4. Squawk (or equivalent linter) is default. As long as you run Postgres, put it in CI.
  5. A working positive down migration is mostly a fantasy. Forward-only plus a sequence of short, safe changes is more realistic than reversible.
  6. The migration tool does not guarantee safety. Pattern + lint + review do.

Finally: the 5-minute outage in the prologue was not the tool's fault. The cause was running ALTER COLUMN ... SET NOT NULL on a 400M-row table inside a single transaction. Keep the tool, change the pattern to expand-contract, and 5 minutes becomes 0 seconds.


References