fireproofsocks
Why do Ecto migrations not use raw SQL and instead rely on Ecto Schemas?
While working with some colleagues who are familiar with other languages and frameworks, they wondered why Ecto migrations don’t use raw SQL and instead typically rely on the Ecto Schemas to insert data. I have never seen any examples of raw SQL in Ecto migrations, but it would make sense to isolate the database “bootstrapping” away from any specific code implementation that might show up in your Schema files.
Any thoughts on this? Thanks!
Most Liked
shanesveller
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.
benwilson512
Seeding data in this case doesn’t seem related to all of this. I view seeds as populating the database for development in which case structs are fine, that’s the present value. It runs after all the migrations run anyway. Otherwise what’s really even the point of having the file? If it’s just the production seeds you did 3 years ago then it’s just a relic with no present value.
I agree about the overall point of not using schemas inside migrations though.
As for why migration files use a DSL and not raw SQL: convenience functions. It lets you do field :foo_id, references(:foos) which helps set up constraints, makes sure the column types are correct, etc.
anthonator
Here are the docs for executing raw SQL from a migration.
benwilson512
Again I think you’re using seeds here in a different sense than the phoenix generator is. If you’re talking about production server bootstrapping then yeah, perhaps those seeds should sit in some kind of migration. That isn’t what seeds.ex is most commonly for. It’s for bootstrapping your development environment with some basic data so you can use the current version of the application. Elixir code makes perfect sense there. Arguably, it should promote using context functions instead of direct inserts however.
Ninigi
I would consider using schemas in your migration as antipattern, for the reasons @shanesveller listed.
Just because I am curious, where did you find an example of this?







