Skip to main content

Cloudsoft Zone Build, deploy and scale with practical guides to PHP, Node.js, React, DevOps and AI automation. Explore the guides

Cloudsoft Zone
DevOpsDevelopmentTutorial

Stop a failed database migration from turning into a broken production deployment

Contain a failed database migration with explicit release gates, schema postconditions and reviewed recovery instead of switching code or retrying blindly.

Nilesh Kushvaha

5 min read

Laravel artwork with a migration failure workflow stopping promotion until schema state and recovery are verified.
Image: Cloudsoft Zone layout; Laravel / Public domain
In this article

A release adds a unique constraint, encounters existing duplicate values and stops halfway through its migration. The deployment script continues to switch code anyway. The application now expects a schema that the database never reached.

A failed database migration should stop promotion at a postcondition gate. When a database migration failed halfway, the guide preserves evidence, repairs state before reconciling history, and uses checks such as prisma migrate status with Laravel and Prisma examples.

A failed database migration should stop promotion at a deliberate release gate. This guide focuses on preventing that second failure and recovering from the actual schema state. It uses Laravel and Prisma ORM 7 examples; other migration systems need their own version-specific procedures.

Our Laravel production checklist covers the wider release process. The distinct concern here is proving the migration outcome before activating dependent code.

Planned statements, migration history and actual schema state jointly establish migration outcome.
A skipped or partially applied operation requires a state check before promotion.

Step 1: Review data assumptions before release

Read the actual generated statements. Look for unique constraints, new non-null columns, type changes, dropped fields, table rewrites and application-managed backfills.

A migration that passes against an empty database may fail against retained data. Use an approved sanitized rehearsal copy with realistic row counts and known edge cases. Confirm privacy controls before copying anything from production.

For a proposed unique invoice reference, a preflight query might identify duplicates in that rehearsal database:

SQL
SELECT external_reference, COUNT(*) AS occurrences
FROM invoices
WHERE external_reference IS NOT NULL
GROUP BY external_reference
HAVING COUNT(*) > 1
LIMIT 50;

The output is bounded, but grouping can still scan substantial data. Evaluate the query plan and workload before running an equivalent check on a live primary. Replace the table and column with the actual reviewed schema.

Decide how to repair duplicates through business rules. Deleting rows to make a constraint pass can discard legitimate invoices and is not a safe generic fix.

Step 2: Understand the database boundary

MySQL supports atomic DDL for documented operations, but that does not make a complete migration file one rollbackable transaction. DDL can implicitly commit, and earlier statements may remain after a later statement fails.

Separate the migration tool's history from the actual schema and data. A failure record describes the attempt; it does not enumerate every business consequence automatically.

Observation Incorrect conclusion Required next evidence
Command failed Nothing changed Inspect applied statements and schema
History says applied All runtime behavior is correct Check required schema and application contract
Backup exists Recovery is immediate Verify restoration procedure and data-loss window
Code rollback succeeds Database returned to old state Check schema compatibility separately

Step 3: Serialize migration ownership

Use one controlled migration owner for a release. Avoid having every application replica execute migrations independently on startup.

Laravel provides isolated migration execution through a cache lock. Its documented behavior includes a successful exit status when another invocation holds the lock and the competing attempt does not execute. The deployment gate must therefore check the intended postconditions, not only process exit.

Use a shared lock backend where the release spans machines. An isolated local cache on each host cannot establish a fleet-wide exclusion boundary.

Schema expansion and its verified postconditions precede code activation.
Do not switch to code that depends on an unverified migration result.

Step 4: Put a postcondition gate before activation

The release procedure should have explicit states. The following sequence is pseudocode for an application-owned deployment adapter, not a generic executable shell script:

Text
Verify exact artifact and migration identity
Acquire the reviewed release-operation lock
Check old-code compatibility and recovery prerequisites
Execute the approved migration procedure
Inspect required migration history and schema postconditions
Stop on failure, skipped outcome or unresolved state
Activate the compatible application artifact
Verify public workflow and worker progress

Postconditions might include a specific column definition, index or constraint plus a harmless query under the application account. Keep checks bounded and tied to the exact migration identity.

Avoid a single chained command that performs migrations and immediately restarts the application without inspecting the result. Also avoid reporting success because the runner reached its final logging statement.

Step 5: Preserve evidence when execution fails

Stop conflicting release attempts and record the active application version. Preserve the command error, migration identifier and non-secret database details in restricted operational records.

Inspect migration state through the correct tool and configuration. Examples below are observational commands for the intended application environment:

Shell
php artisan migrate:status
pnpm exec prisma migrate status --config prisma.config.ts

These commands belong to different stacks; run only the one your application owns. Confirm the configured database before execution and do not dump credentials while diagnosing it.

Determine which statements completed and whether the old runtime remains compatible. Do not assume a retry is idempotent merely because the migration file has the same name.

Step 6: Repair state before reconciling history

Choose forward repair or a reviewed reversal from the observed state and retained data requirements. In Prisma ORM 7, migrate resolve adjusts migration history; it does not perform the missing SQL or recover lost records.

Use the version-specific official recovery procedure and review any generated SQL before applying it. A script that restores schema shape can still discard data or break active readers.

Never use a development reset or fresh migration command against retained production data as an incident shortcut. If restoration is necessary, follow the verified backup procedure with an explicit cutover and reconciliation plan.

Contain the failed operation, inspect actual state and apply a reviewed repair before reconciling history.
Marking a migration resolved does not execute the missing SQL or restore data.

Step 7: Rehearse the gate itself

Include a duplicate-data constraint failure, unavailable lock backend, skipped isolated attempt and partial DDL outcome in an isolated rehearsal. Confirm that dependent code is not activated while state remains unresolved.

After repair, check migration history, required schema and a representative application workflow. Record what changed and why the next promotion is now compatible.

Our MySQL recovery guide explains the recovery foundation. For release gates around your actual migrations and workers, discuss the project with Cloudsoft Zone.

Featured image: Cloudsoft Zone technical layout with laravel artwork by Laravel (Public domain). Product mark resized proportionally for identification; no endorsement implied. Layout: CC BY-SA 4.0.

Frequently asked questions

  • Does MySQL atomic DDL make an entire migration file transactional?

    No. Supported individual statements can be atomic, but DDL can implicitly commit and a multi-statement migration may still leave partial changes.

  • Can Laravel migrate --isolated exit successfully without applying my migration?

    Yes. A competing lock holder can cause another invocation to skip execution while returning success. Verify the intended migration and schema postconditions.

  • Does Prisma migrate resolve repair a failed schema change?

    In Prisma ORM 7 it reconciles migration history. Inspect and correct the actual schema as required before marking the failed migration applied or rolled back.

Sources

  1. Laravel migration execution and isolation — Laravel
  2. MySQL implicit commits — MySQL
  3. MySQL atomic DDL boundaries — MySQL
  4. Prisma ORM 7 hotfix and failed-migration recovery — Prisma
  5. Prisma ORM 7 CLI migration behavior — Prisma

Comments

No comments yet. Be the first to join the conversation.

Leave a comment

Every comment is read by a moderator before it appears. Your email address is never published; it is used only to contact you about this comment.

Never published. Used only to contact you about this comment.

Loading the security check…

Comments are moderated before publication.