Laravel down migrations
I get an email newsletter from Martin Joo every week or so. The newsletters generally have some useful tips around the Laravel framework or sometimes just general development tips. I’ve learned a couple of neat tricks here and there, and will continue to receive.
This morning I received an email with a Laravel ‘tip’ regarding deployment issues to watch out for:
Missing down() method in migrations
If you write a migration but forget about the down() method it can cause some trouble. When a deployment goes wrong and you need to roll back to a previous version it’s crucial to have this process as easy and fast as possible.
The down method in your migration plays a colossal role in deployment rollbacks. If you miss one, your app will be left in an inconsistent state. The code is reverted to the previous version, but your database still have the new schema.
Everyone has opinions, of course, and while this might intuitively seem like a good idea, my own experience has been that having ‘down’ migrations overall are just bad. Having the ability to conveniently remove data or drop tables or indexes in production is simply not a good idea. I’ve seen this do more harm than good, and the ‘harm’ is often quite bad.
If you deploy, then *immediately* understand you have to roll back, and you can guarantee that there’s no new data in the upgraded database (new table, new column, etc), a ‘down’ migration to revert that new change might be acceptable, and whatever crisis you were trying to avoid has been avoided.
But, imagine you’ve done a migration to add a new table, to go with a new screen, and that gets deployed. A couple hours later you find a problem. ‘Rolling back’ – including deleting the new table where new screen data was stored – will literally remove data people have just entered. Why do that?
I was a bit surprised that Martin’s ‘down migration’ tip didn’t raise this as a concern to be aware of.
So… how might you handle rollbacks if there’s a problem?
If you put out some new feature, and you need to roll the code back, can you just disable that particular feature? “Feature flags” are a technique to use to surround functionality with checks to determine whether to expose said functionality. Being able to just flip a bit in a database (or edit a config file on production if need be) is generally much safer than more code changes. Laravel 10 now bundles a set of feature flag behaviour, but there are other packages you can use as well.
If you don’t have feature flags in place, before you roll out, think about what steps you’d need to take to get the code back, but keep the new database schema in place. In many (likely most) cases, your old code will work just as well with a new table or column in place, and there’s usually no reason to revert those items. There *can* be, if you’ve got triggers or views or procedures that connect earlier tables with new tables/columns. In cases like that, an actual rollback of the state of the database might actually be necessary, but even then, a ‘down’ migration to revert 100% of everything may not make sense. For example, you may need to disable or delete a trigger, but leave existing data in the new column(s) in place.
In short, ‘down migrations’ offer a simple way to be more destructive than was intended, and without explicit backup/restore steps in place before a down migration, you may unintentionally lose data.