Why do Ecto migrations not use raw SQL and instead rely on Ecto Schemas?

I would actually assert that using Ecto.Schemas, changeset functions, and/or context modules directly in migrations is dangerous, independent of whether doing pure data insertions in a migration is a good or bad practice - which was part of my position in the forum thread about Seeds as migrations.

My main concern about this practice is because I believe your migrations should remain valid to execute from start-to-finish throughout the life of your project - no shortcuts with mix ecto.dump/mix ecto.load, etc. I also believe that once a migration has been applied anywhere outside of the author’s workstation, you should treat that migration file as largely read-only and not go mucking about with its meaningful content later on. Aesthetic changes to syntax are still okay if the behavior remains the same. This is obviously a subjective position to take, and a fairly militant one.

Now, with that context in mind, here’s how those concerns pertain to this question: If your schemas evolve sufficiently over time, and you’re intentionally using schema structs and/or changeset functions in migration files, you’ll likely eventually experience the problem that the content in your earlier migrations, which were valid at the time, are no longer passing validations or are otherwise failing to execute with the current versions of those schemas/changesets. How do you solve this? Adhering to my constraints above, you nearly have to resort to schemaless Ecto queries or raw SQL.

It’s not often that I get to hold up Ruby/Rails as a positive example for Elixir devs, but they’ve also previously included some notes in their official documentation about the pitfalls of this approach, and techniques to avoid this tight temporal coupling between migrations and ActiveRecord models. It looks like it’s been removed from the latest version of the docs for Rails 5.2, though, but some of the ideas in the historical link are still pertinent.

9 Likes