Migration Long-Term Best Practices

At my job, we currently have a few things we do when writing migrations:

  • Always write the class we are modifying into our migration, so we are not relying on the class as it is, and the migration can be run later even if the model changes (this is also pointed out in the Rails Guides I believe)
  • Keep migrations to basically a single table change per migration, to make it easier to roll-back if one in the middle fails
  • Try to generally write a migration as if it may be run at any point, even years later. Of course we can’t tell what things would be like then, but we can abstain from using specific methods etc (usually if we absolutely have to have them we place them in the model we recreate in the migration)

However, someone on the team recently objected to this as basically being too much non-practical ceremony, since in practice we run the migration once, and use schema.rb whenever we need to build from scratch.

Unfortunately, I can’t seem to find much about what the rails community at large does. Whenever we make a change that goes against “the rails way”, we have a discussion about it so we can make it a clear choice, that we know the cost, etc. But AFAICT, besides the example given in the Rail’s guides RE classes in migrations, there isn’t a lot of advice here. And that example is pretty easily countered - the dev db isn’t mean to be constant, like the prod env, so wiping it and loading with schema.rb makes more sense anyway.

What do you all do or recommend? Do you strive to make migrations a path that can be run at any time, or are they more just history of what occurred?

As with most things, it depends.

For some applications, it’s not likely you’ll be running a migration against a far-future version of the codebase, so it’s not necessarily worth the time to make migrations runnable against any conceivable future version of the codebase. Instead, it’s more convenient to write migrations that are intended to be run against the current codebase.

On the other hand, if you have a lot of developers and a lot of code churn, then it’s likely that your migration will run in a slighty-future version of the codebase where someone may have changed something you depend on in your migration. In this case, it’s helpful to insulate yourself against these kinds of potential changes.

If the team is small, consider going with the convenient option.

If the team is large, consider going with the more time-consuming option as a way to mitigate risk.

Finally, for things like Rails engines where the migrations are only run when your consumers upgrade your engine, it’s likely that migrations are run in a future unknown codebase, so you definitely have to build the migrations in a way that does not depend on the current codebase. GitHub - spree/spree: Open Source multi-language/multi-currency/multi-store eCommerce platform is a good example of this.