189

Very simple question here - if migrations can get slow and cumbersome as an app gets more complex and if we have the much cleaner rake db:schema:load to call instead, why do migrations exist at all?

If the answer to the above is that migrations are used for version control (a stepwise record of changes to the database), then as an app gets more complex and rake db:schema:load is used more instead, do they continue to maintain their primary function?


Caution:

From the answers to this question: rake db:schema:load will delete data on a production server so be careful when using it.

Community
  • 1
  • 1
sscirrus
  • 55,407
  • 41
  • 135
  • 228
  • 5
    +1 I never understood the purpose of migrations; why not just version control the schema? – alternative May 05 '11 at 23:44
  • 9
    @alternative - migrations allow you to do other stuff, like if you need to add a non-null column you can smartly fill that column with data instead of using some default value. – Josh M. Mar 26 '14 at 21:09

9 Answers9

235

Migrations provide forward and backward step changes to the database. In a production environment, incremental changes must be made to the database during deploys: migrations provide this functionality with a rollback failsafe. If you run rake db:schema:load on a production server, you'll end up deleting all your production data. This is a dangerous habit to get into.

That being said, I believe it is a decent practice to occasionally "collapse" migrations. This entails deleting old migrations, replacing them with a single migration (very similar to your schema.rb file) and updating the schema_migrations table to reflect this change. Be very careful when doing this! You can easily delete your production data if you aren't careful.

As a side note, I strongly believe that you should never put data creation in the migration files. The seed.rb file can be used for this, or custom rake or deploy tasks. Putting this into migration files mixes your database schema specification with your data specification and can lead to conflicts when running migration files.

totymedli
  • 29,531
  • 22
  • 131
  • 165
jesse reiss
  • 4,509
  • 1
  • 20
  • 19
  • 95
    thank you for informing that rake db:schema:load deletes all production data! – Magne Mar 12 '12 at 12:06
  • 3
    Rather than replacing "collapsed" migrations with a new one that mimics the schema, I wrote a gem that just clears them out, and prompts users to use `db:schema:load` if they try to run `db:migrate` against a fresh install. @ [**clear_migrations**](https://github.com/ykessler/clear-migrations) – Yarin Oct 01 '13 at 00:33
  • probably an obvious answer but before the first push to production, would you recommend just deleting all the migrations and using db.schema as the first migration? – dtc Jun 16 '15 at 07:36
  • Note: `rake db:schema:load` can also be really useful to setup your test database. – giniouxe Mar 24 '21 at 13:57
35

Just stumbled across this post, that was long ago and didn't see the answer I was expecting.

rake db:schema:load is great for the first time you put a system in production. After that you should run migrations normally.

This also helps you cleaning your migrations whenever you like, since the schema has all the information to put other machines in production even when you cleaned up your migrations.

sscirrus
  • 55,407
  • 41
  • 135
  • 228
ereslibre
  • 493
  • 4
  • 3
  • So you can "cleanse" your migrations because you never have to use them? Sounds like a bizarre statement. – Abe Petrillo Aug 07 '13 at 16:04
  • It's unclear to me what the benefit of `db:schema:load` has other than shaving off a few seconds once throughout the development cycle. Have you ever worked with an app that took longer than 30 seconds to be built up? I'm currently working on an app that has bugs in it's migration files and it will never migrate up without either having bug fixes or running `db:schema:load` which makes me think schema:load is for when somethings gone wrong regarding the app's development. – Ninjaxor Oct 23 '15 at 04:39
  • Another argument I would make for keeping migrations is that rails core team directs users to `instead of editing schema.rb, please use the migrations feature`. So if you're running `db:schema:load` on an auto-generated file that you don't have migrations to auto-generate again, you're effectively going the route of manually "editing" the schema and disusing migrations. I wish I had a citation from the rails guide on this, but they don't discuss schema:load, which dauntingly adds to my frustration in deciding how to approach the schema:load feature. =/ – Ninjaxor Oct 23 '15 at 04:48
  • I came to this page precisely because I agree with that. My experience is that once the site is in production, it is much safer to use migrations to change it. In spite of this, the comments of the beginning of db/schema.rb precisely state the contrary ! (I have the problem at the start of every project because I forget to put db/schema.rb in the .gitignore...) – user1251840 Jul 31 '17 at 12:46
  • @AbePetrillo wow I missed this comments completely. Of course not, what I meant is that you can clean *old* migrations that have been ran on all production machines if you wish. Over the years I always kept them around, but my "helps you cleaning your migrations whenever you like" statement wasn't meaning that "I would never have to use migrations". So, when you deploy a new machine, run `rake db:schema:load` as opposed to `rake db:migrate`. Then from there on, you can `rake db:migrate`. – ereslibre Feb 23 '18 at 14:14
8

Migrations lets you add data to the db too. but db:schema:load only loads the schema .

Shaunak
  • 17,377
  • 5
  • 53
  • 84
  • isn't adding data through migrations bad practice? – ian root Apr 30 '21 at 01:31
  • 1
    Almost always yes. There are some cases when it can come in handy in very initial development phases to iterate quickly. This answer is just pointing out the difference though. – Shaunak Jan 22 '22 at 16:07
6

Because migrations can be rolled back, and provide additional functionality. For example, if you need to modify some data as part of a schema change then you'll need to do that as a migration.

Jamie Penney
  • 9,424
  • 3
  • 29
  • 37
5

As a user of other ORM's, it always seemed strange to me that Rails didn't have a 'sync and update' feature. ie, by using the schema file (which represents the entire, up-to-date schema), go through the existing DB structure and add/remove tables, columns, indexes as required.

To me this would be a lot more robust, even if possibly a little slower.

Dan James
  • 51
  • 1
  • 1
  • 1
    The task to migrate database with data from one complicated schema to another is not trivial some times. It may not be resolved automatically and data may not be consistently migrated with single step. Rails migration is master and schema is dependent. Schema automatically recreated with each migration but not vice versa. – oklas Mar 26 '16 at 12:30
  • Rails own guides explicitly states that `schema` is the master, not migrations. – Drenmi May 31 '18 at 06:04
0

I have already posted as a comment, but feels it is better to put the comments of the db/schema.rb file here:

# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

Actually, my experience is that it is better to put the migration files in git and not the schema.rb file...

user1251840
  • 655
  • 5
  • 8
0

rake db:migrate setup the tables in the database. When you run the migration command, it will look in db/migrate/ for any ruby files and execute them starting with the oldest. There is a timestamp at the beginning of each migration filename.

Unlike rake db:migrate that runs migrations that have not run yet, rake db:schema:load loads the schema that is already generated in db/schema.rbinto the database.

You can find out more about rake database commands here.

Nesha Zoric
  • 6,218
  • 42
  • 34
0

So schema:load takes the currently configured schema, derives the associated queries to match, and runs them all in one go. It's kind of a one-and-done situation. As you've seen, migrations make changes step-by-step. Loading the schema might make sense when working on a project locally, especially early in the lifetime of a project. But if we were to drop and recreate the production DB each time we do a deployment, we would lose production data each time. That's a no-go. So that's why we use migrations to make the required changes to the existing DB.

So. The deeper into a project you get, the more migrations you'll get stacked up as you make more changes to the DB. And with each migration, those migrations become more and more the source of truth of what's on production - what matters isn't what's in the schema, but what migrations have been run in production. The difference is effectively moot if we have both in sync. But as soon as one goes of out date from the other, you start to have discrepancies. Ideally this would not happen, but we live in the real world, and stuff happens. And if you're using schema:load to set up your DB locally, you might not be getting the actual state of the DB, as it is reflected via the migration history on production.

fuyi
  • 2,573
  • 4
  • 23
  • 46
0

rake db:schema:load is used to create the database schema from the schema.rb file. The schema.rb file is a representation of the database schema in Ruby code. This command is typically used when setting up a new database or when a fresh start is needed.

rake db:migrate, on the other hand, is used to apply new database migrations to an existing database. Database migrations are used to make changes to the database schema over time. These changes can include creating new tables, modifying existing tables, or adding new columns to existing tables.

6reg
  • 1
  • 1
  • 2