偶尔会有人在我们的某个开源包上提交 PR,想要给迁移文件加上一个 down 方法。我通常会快速关闭这些 PR,并附上感谢和一句解释:“我们的项目中不使用回滚迁移”。
回滚迁移看起来像一张安全网,但它往往是一种虚假的安慰,可能带来的问题比解决的问题还多。
与其在每个 PR 里单独解释,不如让我分享一下我们不写回滚迁移的原因,以及我们实际的做法。
未经测试的代码问题
回滚迁移有一个独特之处:它们很可能是大多数 Laravel 应用程序中测试最少的代码。当功能、API 和业务逻辑在大多数项目中都经过测试时,down 方法却常常是一次编写,然后就被遗忘了。
想想看——你上次在测试套件中运行回滚迁移是什么时候?或者验证过复杂的数据转换是否真的能正确逆转?很可能你没这么做过。测试回滚场景很困难,而且通常感觉像是在为极少发生的事情做准备。
新数据怎么办?
回滚迁移的一个棘手之处在于,部署后产生的新数据并不会凭空消失。如果你的应用很繁忙,用户会在部署后非常迅速地与你的数据库交互。
如果你添加了一个新表,并且用户已经创建了记录,那么回滚就意味着这些数据无处可去。当你把数据从一种格式转换为另一种格式时,原始格式可能就永远丢失了。即使是像将姓名字段拆分为名和姓这样看似简单的更改,在需要逆转时也会变得复杂,尤其是当用户已经更新了他们的信息之后。
保持代码与数据库的和谐
你的应用程序代码期望特定的数据库结构,当数据库模式改变而代码没有同步更新时,可能会以意想不到的方式出问题。
模型可能引用了不存在的列,控制器可能查询了已被删除的表,验证规则可能检查了已消失的字段。对于现代的部署策略(如容器编排和蓝绿部署),这种复杂性会成倍增加,因为系统的不同部分可能运行着不同版本的代码。
向前进
在 Spatie,我们多年来一直采用“只进不退”的迁移方式。
当某些事情需要逆转时,我们会首先根据具体情况仔细思考合适的解决方案。如果需要,我们会精心编写一个新的迁移来推动我们前进,而不是试图逆转历史。
结语
下次你忍不住想写那个 down 方法时,问问自己:这段代码真的会运行吗?就算运行了,它真的能正常工作吗?最重要的是,把时间花在确保 up 迁移和新代码坚如磐石上,是不是更有价值?
选择权在你!