1 · Compare the schema
Objective — before touching anything, find out exactly what the new release will change in the database, so a quiet DROP COLUMN or DROP TABLE can never reach production by surprise.
Background
Section titled “Background”A vendor update is two things at once: new files and new migrations. The files you can branch and revert. The migrations touch live data — and that is where the irreversible damage hides. Comparing the schema first answers one question with certainty: “what will change, and can any of it lose data?”
You compare against your frozen baseline — the author-vX.X.X snapshot you tagged when you first imported the app. Laravel migrations track intent (the files you wrote); a schema-diff tool like Atlas tracks reality (the actual tables and columns). You want both, because vendor installers and manual hotfixes can change the database without ever writing a migration file — and only a reality-based diff catches that drift.
flowchart TD A["migrate:status<br/>what's pending?"] --> B["migrate --pretend<br/>preview the SQL"] B --> C["atlas schema diff<br/>reality vs baseline"] C --> D{"Outputs<br/>match?"} D -->|Yes| E["Classify by risk"] D -->|No| F["Schema drift —<br/>investigate"] E --> G{"Any drop or<br/>truncate?"} G -->|No| H["Safe to apply"] G -->|Yes| I["Back up + get approval"]1. Read the vendor changelog first
Section titled “1. Read the vendor changelog first”Before any command, find out what the vendor says changed. The changelog tells you which tables and features to expect in the diff — so an unexpected change stands out instead of blending in.
-
Note what the release claims to touch. New tables, renamed columns, dropped features, security patches. Write the short list somewhere you’ll see it while diffing.
- ✅ You have a one-line summary of what the vendor says this release changes in the database.
A changelog is a claim, not proof — the diff in the next steps is what you actually trust. But reading it first turns the diff from a wall of SQL into a checklist you can confirm against.
2. List the pending migrations
Section titled “2. List the pending migrations”Find every migration the new release will run but yours has not. This is the file-level view: what the vendor intends to do.
-
Check migration status, then isolate the pending ones.
Terminal window php artisan migrate:status --no-ansiphp artisan migrate:status --no-ansi | grep "No"# Expected: every "Ran? No" row is a migration this update will apply- ✅ You have the list of pending migrations; an all-
Ran? Yesresult means there’s nothing new at the file level (skip to step 4).
- ✅ You have the list of pending migrations; an all-
-
Document each pending migration — its file, what it creates or alters, and a first-pass risk read.
Field Example File 2024_11_02_add_timezone_to_users_table.phpTouches userstable — addstimezonecolumnRisk Low (ADD COLUMN) - ✅ Each pending migration has a row capturing file, what it touches, and a first risk read.
3. Preview the SQL with --pretend
Section titled “3. Preview the SQL with --pretend”--pretend prints the SQL a migration would run without running it. This is the safest way to read intent — no rows change, but you see every statement.
-
Print the migration SQL without executing it, and save the output to compare against the Atlas diff.
Terminal window php artisan migrate --pretend# Expected: the CREATE / ALTER statements that WOULD run — nothing is applied- ✅ You have the exact SQL Laravel intends to run, saved for the match check in step 5.
4. Run the Atlas schema diff
Section titled “4. Run the Atlas schema diff”Atlas compares the actual database state against your tagged baseline. This catches changes that never appear in a migration file — installer-created tables, manual SQL, vendor scripts.
-
Diff the post-update state against your frozen baseline tag.
Terminal window atlas schema diff \--from "atlas://CUSTOJO?tag=author-vX.X.X" \--to "atlas://CUSTOJO?tag=author-vY.Y.Y"# Expected: a list of CREATE / ALTER / DROP statements — the real delta- ✅ Atlas prints the concrete schema delta between your baseline and the new release.
-
Open the visual diff in the browser when the text output is dense.
Terminal window atlas schema diff \--from "atlas://CUSTOJO?tag=author-vX.X.X" \--to "atlas://CUSTOJO?tag=author-vY.Y.Y" -w# Expected: a browser tab opens with a side-by-side visual comparison- ✅ The visual diff renders, making added/removed/modified tables easy to scan.
5. Confirm the two views agree
Section titled “5. Confirm the two views agree”Laravel’s --pretend (intent) and Atlas’s diff (reality) should describe the same change. A mismatch means the database holds something your migrations don’t — schema drift you must explain before you apply anything.
-
Line up the two outputs and confirm each change appears in both.
Laravel --pretendAtlas diff Match? CREATE users_zaj+ users_zaj✅ ALTER settings.value~ settings.value✅ (nothing) DROP old_table❌ investigate - ✅ Every change is present in both views; nothing appears in one but not the other.
-
If they disagree, capture the drift as a migration rather than ignoring it — then re-diff until both views agree.
Terminal window atlas migrate diff capture_drift \--dir "file://database/migrations" \--to "mysql://root:@localhost:3306/database" \--dev-url "docker://mysql/8/dev"# Expected: a new migration file describing the un-tracked change- ✅ Drift is captured in a migration (or explained), and a re-run of the diff shows the two views now agree.
6. Classify every change by risk
Section titled “6. Classify every change by risk”Sort each confirmed change into a risk tier. The tier decides what happens next: low-risk changes proceed, destructive ones force a backup and explicit approval before Workflow step 2.
-
Assign a risk tier to each change and decide the action it triggers.
Change Risk Action CREATE TABLE🟢 Low Proceed ADD COLUMN(nullable)🟢 Low Proceed ADD COLUMN(not null)🟡 Medium Verify a default exists MODIFY COLUMN🟡 Medium Test on staging RENAME🟡 Medium Verify code references DROP COLUMN🔴 High Back up first DROP TABLE🔴 Critical Backup + explicit approval TRUNCATE🔴 Critical Block until reviewed - ✅ Every change has a risk tier and a decided action; no change is unclassified.
-
Record the decision so the update has an audit trail — what changes, whether any are destructive, and whether a backup is required.
- ✅ A short written record names the changes, flags any destructive ones, and states whether a pre-update backup is needed.
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 👤 Changelog read — the vendor’s claimed DB changes are noted and available while diffing.
- 🤖 Pending migrations listed —
migrate:statusran; each pending migration documented (file, touches, risk). - 🤖 SQL previewed —
migrate --pretendoutput saved for the match check. - 🤖 Atlas diff run — the new release diffed against the
author-vX.X.Xbaseline; driver matched. - 🤖 Views agree — Laravel
--pretendand Atlas diff describe the same change, or drift is captured. - 🔀 Changes classified — every change has a risk tier; destructive changes flagged for backup + 👤 approval.