case studies

Compare Staging and Production MySQL Schemas Before a Release

Release day for a team on MySQL. The new code has been tested on staging for a week, and staging has had the release’s migrations for a week. The question before shipping is a plain one: what does staging have that production doesn’t, and what does production have that nobody put in staging?

Migration tools answer half of it, the half they applied. RadarModel Schema’s diff reads both databases as they are and lists every difference, whoever made it.

Four steps, from configuring both databases to releasing or reconciling, next to a diff with a new column and a widened column on staging, an index found only on production, and exit 1.

One command, both databases, every difference.

The setup

Both databases go in one config, each with its own connection string from the environment:

{
  "databases": [
    {"name": "prod", "driver": "mysql", "dsn_env": "PROD_MYSQL_DSN"},
    {"name": "staging", "driver": "mysql", "dsn_env": "STAGING_MYSQL_DSN"}
  ]
}

On MySQL and MariaDB the user needs SELECT, SHOW VIEW and TRIGGER on each database, so a hidden table is never mistaken for a missing one.

What the diff found

This is the released binary against two real MariaDB databases, app and app_staging. Staging has the release’s migration: a new currency column and a wider status. Production has an index someone added by hand a month ago.

$ radarmodel-schema diff prod staging
RadarModel Schema: 1 breaking, 2 warnings: what staging has that differs from prod
prod vs staging  WARNING   column app.orders.currency          new
prod vs staging  BREAKING  column app.orders.status            type varchar(20) -> varchar(40)
prod vs staging  WARNING   index app.customers.customers_name  gone
$ echo $?
1

The two databases have different names, app and app_staging, and the diff lines them up before comparing, so the report talks about app.orders on both sides.

The first two lines are the migration, as expected. Widening a column is usually harmless, but RadarModel Schema calls every type change breaking because it can’t know what the app does with it. Here it’s a reminder that the migration has to run before the new code. The third line is the surprise. Production has an index staging never had, so the release was tested without it, and a migration that rebuilds customers would quietly drop it.

diff exits 1 when the databases differ and 0 when they match, so it can sit in a release checklist or a pipeline. The reference has the details.