Consolidating Rails Migrations on a Long Running Project - ruby-on-rails

I'm working on a project that's accumulated hundreds of migrations, and I'm unsure what to do with them long term. I suppose they're no hurting anything, but it seems strange to keep around a bunch of old files for incremental migrations, some of them creating tables that are later removed.
So far, I've seen three possibilities:
Leave them alone. They're not hurting anything.
Just delete them. I don't see much harm in doing this, since a new developer would be probably be starting with schema load anyway, not migrations.
Delete them all, create a new one with a timestamp matching an old merge, and create a new merge from your schema. This seems very clean, but I'm not sure who would actually use it.
I'm inclined to just delete them, but I'm curious if there's a big pitfall I'm missing.

In my opinion, as soon as every database on the project, especially the production, are at least at version '201xxxxxxxx', it should be fine to delete migrations before that version. They are not technically necessary anymore.
After that, if you want to play archaeology with your database history, you can still use your version control system.
With Git for example, you can use the following commands to have a quick look over the past :
git log --name-only db/migrate/ #to list commit involving migrations + migration filename
git show xxxxx db/migrate #to see the code of commit xxxxx's migration(s)
Alternatively, you can browse repository history of schema.rb, identify a commit and see the corresponding migration content with the command above.
If you prefer to have a lighter db/migrate and use Version control, I would go for a little cleanup.
If you find it more convenient to have the whole migrations history directly available because it's easier to browse, I would go for option 1.
Note: it is very likely that old migrations do not make sense with the current application code. For example, some migrations may refer to class or methods that don't exist anymore.
Using version control to checkout the application at the time the migration was written could also avoid some confusion.

Personally, I lean toward option 1: It's generally true that at some point in any project, the schema is what matters, and the migrations are just a curiosity, but you're right when you say they're not hurting anything. Theoretically the old migrations could be useful for someone who wanted to go back and see how the database was organized at some point in the past.
I don't know of any serious pitfalls in deleting them, but I also don't see an advantage to doing so, unless it's the saving of time scrolling past them when you want to edit a new migration.
I don't think the effort of putting together a single migration that duplicates the schema is beneficial - it's extra work, and that's what the schema is for anyway.

If you work on a project large enough for long enough, there will come a time when you look at all of those extra migrations with disdain and wonder how so many can exist.
You think to yourself, "just delete them...they don't do anything." This is a perfectly logical and normal thought process (especially as a Rails developer, we just love to minimize code and make things efficient!), but don't let the dark side tempt you.
By deleting migrations, you are deleting the historical record of your application's data model, and worse, the logical path you took to get to your current model. This history can help you remember why you did what you did, and didn't do what you didn't do.
Yes, we've all been guilty of deleting migrations from time to time. But you must resist the temptation as the net benefit would only be a few kB and a cleaner migration folder.
Remember:
Those who delete migrations are condemned to repeat them!

In one of our former projects we dropped all migrations, created a new one which would truncate schema_migrations table with manual sql and then copied db/schema.rb contents to it.
Surely this migration is irreversible, however it allowed us to get rid of hundreds of old valueless migrations but still be able to re-create db not from db schema only but from migrations as well.

Related

Rails Migrations Convention

I've started working with rails recently and I have noticed that during development you can end up with a lot of migration files as your ideas change and your database with it. Is there a convention in rails to avoid accumulating a lot of migrations and deleting redundant ones (such as one migration adding a table and then the next deleting it)? Or, is it preferred to keep all your migrations and never delete them? Thanks.
If you are working on a team it is preferred and you always should keep all the migrations.
Suppose you push a migration to Git and later found out that it needs some change. So you revert and delete the migration and create a new one and push the new changes. It works well in your machine. But if somehow your previous faulty migrations are run by any of your team mate and he pull the new code he will have to run the new migrations which will break the applications in your teammates machine.
But if you are working solo and want to get a bit tidy then you can carefully delete migrations after reverting them (Not preferred).
The primary job of migration files is to alter the database schema. Deleting redundant ones (such as one migration adding a table and then the next deleting it) will not cause any problems and it free up some disk space and slug size.

Ruby on Rails: Long list of migration files, common?

Is having a long list of migration files often common when building a web application? I seem to be adding up a long list of migration files because I keep forgetting or keep thinking of adding an extra column to a 'already' migrated database table.
Having a long list of migration files is normal. It's one of the best features of rails. Think of them as layers(like an onion) that you stack on top of each other. If you add a new column or table and then you decide that you don't want it anymore you can rollback(peel away) the latest changes. As long as you have the migration files you can move back and forth easily(don't recommend moving much but you get the point). REMEMBER DO NOT DELETE migration files once they are raked unless you do a rollback. When you rollback and delete a migration file make absolutely SURE you are at the right layer(rollback point).
why? because for example when someone clones your app and runs your migration file it goes through all the migration files from beginning to end. if something in the middle is messed up or deleted you won't be able to create the database because it goes through ALL the steps. Hope it helps.
It may be bad habit, but I migrate down using rake db:migrate VERSION=0, then change the respective migration that has (for instance) the users, and finally migrate the database using rake db:migrate. That way i have less of a mess, and know exactly which migration does what to what model. It's cleaner, but I guess this technique can be used only at the beginning of the webapp.
Hope this helps.
One more point to be noted, from rails docs
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 to check this file(schema.rb) into your version control system.
IMO, if you do the migrations correctly it should have to have a long list of migrations. Because every little change you have to do via migration. As you said, proper way is to add a new migration when you need to alter a table.
So as I mentioned I believe having more and more migrations means you are doing it correct. (Because most of the time when you need alterations to to an existing table you simple cannot drop the table and re-create it).
But having said that it always a good idea to run rake db:migrate every now and then (for an isolated db) just to make sure your migrations are working as a group.

rake db migration issues

Some questions on db migration tasks (rake db:migrate)
Does it make sense to rename the file names, if there is a spelling mistake.
(e.g. CreaetFoos.rb to CreateFoos.rb)
I created a migration script (say version '3') by mistake during the dev process and I would like it to be removed from git. What if I had already migrated to be at the current level of '6', should I just rollback till '2', remove migration script corresponding to '3' from git and re-run migration scripts. Will schema_migrations hold the right data in this case?
I would like to create a migration script during the dev process, but I don't want this to be considered as part of the migration script unless I call it complete (i.e. I don't want other developers to use a incomplete migration script which is checked into git). How do I handle this?
A multi-part question! Let me answer them in the proper parts.
[Question 1] Does it make sense to rename the file names, if there is a spelling mistake.
If it bothers you that much, yes. It would also bother me too.
[Question 2] [Wall of text about removing a migration]
Once a migration has been committed to your version control system, it should remain untouched. If it's modified, then you and other developers would need to roll it back and forward in order to get its changes again. It would be much better if you were to never touch old migrations and to fix any issues in new migrations. There are exceptions to this rule, which will be obvious when you encounter them.
Such as migrations that drop entire tables by accident.
[Question 3] Handling of migrations committed to version control
It's best practice to work in your own branch if you're going to be committing work that is incomplete. By doing this, you will leave the main branch ("master", probably) pristine and complete, allowing for other developers to continue on their own work.
Once you've got that migration sorted, then you will merge that branch back into master.

Is it a good idea to purge old Rails migration files?

I have been running a big Rails application for over 2 years and, day by day, my ActiveRecord migration folder has been growing up to over 150 files.
There are very old models, no longer available in the application, still referenced in the migrations. I was thinking to remove them.
What do you think? Do you usually purge old migrations from your codebase?
The Rails 4 Way page 177:
Sebastian says...
A little-known fact is that you can remove old migration files (while
still keeping newer ones) to keep the db/migrate folder to a
manageable size. You can move the older migrations to a
db/archived_migrations folder or something like that. Once you do trim
the size of your migrations folder, use the rake db:reset task to
(re-)create your database from db/schema.rb and load the seeds into
your current environment.
Once I hit a major site release, I'll roll the migrations into one and start fresh. I feel dirty once the migration version numbers get up around 75.
I occasionally purge all migrations, which have already been applied in production and I see at least 2 reasons for this:
More manageable folder: it is easier to spot a new migration.
Cleaner text search results: global text search within a project does not lead to tons of useless matches because of some 3-year-old migration when someone added or removed some column which anyway does not exist anymore.
They are relatively small, so I would choose to keep them, just for the record.
You should write your migrations without referencing models, or other parts of application, because they'll come back to you haunting ;)
Check out these guidelines:
http://guides.rubyonrails.org/migrations.html#using-models-in-your-migrations
Personally I like to keep things tidy in the migrations files. I think once you have pushed all your changes into prod you should really look at archiving the migrations. The only difficulty I have faced with this is that when Travis runs it runs a db:migrate, so these are the steps I have used:
Move historic migrations from /db/migrate/ to /db/archive/release-x.y/
Create a new migration file manually using the version number from the last run migration in the /db/archive/release-x.y directory and change the description to something like from_previous_version. Using the old version number means that it won't run on your prod machine and mess up.
Copy the schema.rb contents from inside the ActiveRecord::Schema.define(version: 20141010044951) do section and paste into the change method of your from_previous_version changelog
Check all that in and Robert should be your parent's brother.
The only other consideration would be if your migrations create any data (my test scenarios contain all their own data so I don't have this issue)
Why? Unless there is some kind of problem with disk space, I don't see a good reason for deleting them. I guess if you are absolutely certain that you are never going to roll back anything ever again, than you can. However, it seems like saving a few KB of disk space to do this wouldn't be worth it. Also, if you just want to delete the migrations that refer to old models, you have to look through them all by hand to make sure you don't delete anything that is still used in your app. Lots of effort for little gain, to me.
See http://edgeguides.rubyonrails.org/active_record_migrations.html#schema-dumping-and-you
Migrations are not a representation of the database: either structure.sql or schema.rb is. Migrations are also not a good place for setting/initializing data. db/seeds or a rake task are better for that kind of task.
So what are migrations? In my opinion they are instructions for how to change the database schema - either forwards or backwards (via a rollback). Unless there is a problem, they should be run only in the following cases:
On my local development machine as a way to test the migration itself and write the schema/structure file.
On colleague developer machines as a way to change the schema without dropping the database.
On production machines as a way to change the schema without dropping the database.
Once run they should be irrelevant. Of course mistakes happen, so you definitely want to keep migrations around for a few months in case you need to rollback.
CI environments do not ever need to run migrations. It slows down your CI environment and is error prone (just like the Rails guide says). Since your test environments only have ephemeral data, you should instead be using rake db:setup, which will load from the schema.rb/structure.sql and completely ignore your migration files.
If you're using source control, there is no benefit in keeping old migrations around; they are part of the source history. It might make sense to put them in an archive folder if that's your cup of coffee.
With that all being said, I strongly think it makes sense to purge old migrations, for the following reasons:
They could contain code that is so old it will no longer run (like if you removed a model). This creates a trap for other developers who want to run rake db:migrate.
They will slow down grep-like tasks and are irrelevant past a certain age.
Why are they irrelevant? Once more for two reasons: the history is stored in your source control and the actual database structure is stored in structure.sql/schema.rb. My rule of thumb is that migrations older than about 12 months are completely irrelevant. I delete them. If there were some reason why I wanted to rollback a migration older than that I'm confident that the database has changed enough in that time to warrant writing a new migration to perform that task.
So how do you get rid of the migrations? These are the steps I follow:
Delete the migration files
Write a rake task to delete their corresponding rows in the schema_migrations table of your database.
Run rake db:migrate to regenerate structure.sql/schema.rb.
Validate that the only thing changed in structure.sql/schema.rb is removed lines corresponding to each of the migrations you deleted.
Deploy, then run the rake task from step 2 on production.
Make sure other developers run the rake task from step 2 on their machines.
The second item is necessary to keep schema/structure accurate, which, again, is the only thing that actually matters here.
It's fine to remove old migrations once you're comfortable they won't be needed. The purpose of migrations is to have a tool for making and rolling back database changes. Once the changes have been made and in production for a couple of months, odds are you're unlikely to need them again. I find that after a while they're just cruft that clutters up your repo, searches, and file navigation.
Some people will run the migrations from scratch to reload their dev database, but that's not really what they're intended for. You can use rake db:schema:load to load the latest schema, and rake db:seed to populate it with seed data. rake db:reset does both for you. If you've got database extensions that can't be dumped to schema.rb then you can use the sql schema format for ActiveRecord and run rake db:structure:load instead.
Yes. I guess if you have completely removed any model and related table also from database, then it is worth to put it in migration. If model reference in migration does not depend on any other thing, then you can delete it. Although that migration is never going to run again as it has already run and even if you don't delete it from existing migration, then whenever you will migrate database fresh, it cause a problem.
So better it to remove that reference from migration. And refactore/minimize migrations to one or two file before big release to live database.
I agree, no value in 100+ migrations, the history is a mess, there is no easy way of tracking history on a single table and it adds clutter to your file finding. Simply Muda IMO :)
Here's a 3-step guide to squash all migrations into identical schema as production:
Step1: schema from production
# launch rails console in production
stream = StringIO.new
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream); nil
stream.rewind
puts stream.read
This is copy-pasteable to migrations, minus the obvious header
Step 2: making the migrations without it being run in production
This is important. Use the last migration and change it's name and content. ActiveRecord stors the datetime number in it's schema_migrations table so it knows what it has run and not. Reuse the last and it'll think it has already run.
Example: rename 20161202212203_this_is_the_last_migration -> 20161202212203_schema_of_20161203.rb
And put the schema there.
Step 3: verify and troubleshoot
Locally, rake db:drop, rake db:create, rake db:migrate
Verify that schema is identical. One issue we encountered was datetime "now()" in schema, here's the best solution I could find for that: https://stackoverflow.com/a/40840867/252799

Rails migration management - best practices?

What are best practices for migration management?
For instance, when debugging a migration, do you edit the original migration or add an edit migration before committing to the repository? Thanks!
I tend to edit the original migration as long as it is a) the last migration and b) not in source control. This presents a clean migration path for all other consumers of the code. The important thing is that your migrations should be able to run without error from whatever database state is the earliest that you can expect to encounter.
Start testing your migrations.
http://blog.carbonfive.com/2011/01/27/start-testing-your-migrations-right-now/
If you are working with multiple developers, editing an existing migration can be dangerous.
If your coworker has already migrated the original migration, then when he updates he will not pick up the new code and hilarity will ensue. This is a very difficult issue to track down. Error on the side of being a good denizen and just create an updated migration.
Only ever edit an existing migration if you can verify that it has not yet been run by other developers or some automated build setup. To be on the safe side, you shouldn't edit a commited migration file unless the bug was so severe that the migration wouldn't run in the first place (in which case why did you commit it?)
Also, special care has to be taken with migrations calling code from elsewhere in your application so that when they are run, they are run using the correct version of the code. Otherwise subtle changes in your models can really screw up your earlier migrations.
Even after reading this and the answers below, I just learned the hard way. Not to edit the original. You end up losing track your development process and it's hard to get back under control.

Resources