Why I don't use down migrations
Every once in a while, someone opens a PR on one of our open source packages adding a down
function to the migration. I usually close those PRs fast with a thank you and a message “We don’t use down migrations in our projects”.
While down migrations might seem like a safety net, they're often a false comfort that potentially creates more problems than they solve.
Instead of explaining this in every PR separately, let me share why we don't write down migrations and what we do instead.
The untested code problem
Down migrations are unique in that they're probably the least tested code in most Laravel applications. While features, APIs, and business logic will get tested in most projects, down methods tend to be written once and then forgotten.
Think about it - when was the last time you ran a down migration in your test suite? Or verified that complex data transformations actually reverse correctly? Probably you don’t do this. Testing rollback scenarios is hard and often feels like preparing for something that rarely occurs.
What about new data?
The tricky thing about rolling back migrations is that data created after deployment doesn't simply disappear. When you have a busy application, users will interact very fast with your database after deploy.
If you've added a new table and users have created records, rolling back means that data has nowhere to go. When you've transformed data from one format to another, the original format might be lost forever. Even seemingly simple changes like splitting a name field into first and last names become complex when you need to reverse them after users have updated their information.
Keeping code and database in harmony
Your application code expects a certain database structure, and when the database schema changes without the code changing too, things can break in unexpected ways.
Models might reference columns that no longer exist, controllers could query tables that have been dropped, and validation rules might check fields that have vanished. The complexity multiplies with modern deployment strategies like container orchestration and blue-green deployments, where different parts of your system might be running different code versions.
Go forward
At Spatie, we've embraced forward-only migrations for many years now.
When something needs to be reversed, we will first think carefully about the appropriate solution for the particular situation we’re in. If necessary, we’ll handcraft a new migration that moves us forward rather than trying to reverse history.
In closing
The next time you're tempted to write that down method, ask yourself: will this code ever run? Will it actually work if it does? And most importantly, wouldn't your time be better spent making sure the up migration and new code is rock solid?
The choice is yours!