在开发 Laravel 项目的过程中,经常有同事会在我们的开源项目中提交 PR,给 migration 文件添加 down
方法。对于这类 PR,我通常都会礼貌地关闭,并回复:"感谢贡献,但我们团队不使用 down migrations"。
看起来 down migrations 是一个保险措施,但实际上它更像是一种虚假的安全感,往往会带来更多问题。
与其每次都要单独解释这个问题,不如写篇文章聊聊我们为什么不写 down migrations,以及我们的替代方案。
Down migrations 有个很尴尬的特点:它们可能是整个 Laravel 项目中最少被测试的代码。业务功能、API 接口这些核心逻辑大家都会认真测试,但 down 方法?写完就忘了。
仔细想想,你上次在测试环境跑 down migration 是什么时候?你验证过复杂的数据转换能够正确回滚吗?估计很少有人会这么做。回滚场景的测试既困难又麻烦,而且总感觉是在为一个几乎不会发生的情况做准备。
回滚 migration 最麻烦的地方在于:部署后产生的新数据不会凭空消失。在高并发的生产环境中,用户会在你部署完成后立即开始操作数据库。
假设你新增了一张表,用户已经在里面创建了很多记录,这时候回滚意味着这些数据将无处安放。再比如你把数据从一种格式转换成另一种格式,原始格式可能早就丢失了。即便是看起来很简单的操作,比如把姓名字段拆分成姓和名两个字段,当用户已经更新过个人信息后,想要回滚也会变得异常复杂。
应用代码依赖特定的数据库结构,当数据库结构变了但代码没变时,就会出现各种意想不到的问题。
Model 可能会引用已经不存在的字段,Controller 可能会查询已经被删除的表,数据验证规则可能会检查已经消失的字段。在现代化的部署环境中(比如容器化、蓝绿部署),不同服务可能运行着不同版本的代码,这会让问题变得更加复杂。
在 Spatie 团队,我们已经坚持"只向前"的 migration 策略很多年了。
当确实需要"回滚"某些变更时,我们会仔细分析当前的具体情况,然后制定合适的解决方案。如果确有必要,我们会编写新的 migration 来向前修复问题,而不是试图逆转历史。
下次当你想写 down 方法的时候,不妨问问自己几个问题:这段代码真的会被执行吗?即使执行了,它真的能正常工作吗?最重要的是,你是不是应该把时间花在确保 up migration 和新功能代码足够稳定上?
决定权在你手里!