Rails migrations are a somewhat contentious bunch. On one hand they provide a consistent way of provisioning your database, and on the other hand they like to conflict with each other if there’s a heavy stream of development due to a simplistic naming convention. Just in is a change that will name your migrations based on a more unique UTC-based timestamp instead of just a sequential ordering.
When you go to create your next migration you’ll see something like this:
1 2 |
> script/generate migration one
create db/migrate/20080402122512_one.rb |
Instead of the migration file being named 001_one.rb it now has a more unique prefix that will less likely conflict with another migration somebody else happened to check-in around the same time.
Update: When you do an svn update and get new migrations, even if they have a timestamp previous to a migration you’ve already added and run in your own environment, rake db:migrate will intelligently apply all migrations that have not yet run. This eliminates the interleaved migrations issue that existed on the first rev of this feature (see comments).
Another change that accompanies this functionality is the addition of rake db:migrate:up and rake db:migrate:down tasks, which will let you run the up and down operations of an individual migration (that may have been added on an svn merge or update):
1 2 3 4 5 |
> rake db:migrate:up VERSION=20080402122523 == 20080402122523 Two: migrating ============================================== -- create_table(:two) -> 0.0122s == 20080402122523 Two: migrated (0.0124s) ===================================== |
This functionality was previously available as the enhanced migrations plugin, so if you’re on an older version of Rails you can still make your migrations less whiny.
And if you’re just not lovin’ this (as some of the comments seem to mildly indicate) you can always revert back to the sequential prefix naming style of migrations with this guy in the config block of your environment.rb file:
config.active_record.timestamped_migrations = false |
tags: ruby, rubyonrails

I don’t get it :) Is enhanced_migrations supposed to be broken in Rails 2.0 or something? We’re using it (enhanced_migrations) in rails 2.0, and it works awesome.
Am I missing something? :)
I’ll wait and see how this works out; it is a solution but I can’t say that I’m sure it is the best one. I don’t like the idea of timestamp based version numbers because there is still the possibility of collisions (very rarely), but mostly because moving up and down between versions requires an “ls” and copy and paste of the appropriate timestamps.
I’ve always been of the opinion that there should have been a script tagged on to the end of “svn up” that would look in your development DB, decrement your schema version number as needed, move your unchecked in migrations out of the directory, run the new migrations, move your migrations back into the directory, and update their filenames. Simple, hacky, but no collisions, and you keep the version number goodness. I always meant to write this but with enhanced_migrations now a part of Rails I probably won’t bother.
Ummm.. I hope this is an april fools joke
I don’t see how the timestamps help in any way. Can anyone describe a case in which they do?
The biggest problem I see is that migrations will still conflict when multiple people commit them, but now you can’t tell which ones those are without looking through logs. Not to mention that this seems to unnecessarily complicate finding the migration you just added in an Open dialog to edit it.
Tom: From my understanding… This prevents you from creating 15_AddPersonTable, while I’m working on 15_AddArticleTable. Once we both check in our code there will be 2 migrations with the version number of 15. Instead you would create 20080402122523_AddPersonTable, and I would be working on 2008040202312311_AddArticleTable. Now the version numbers are different.
That said, I can still see a problem when you create a migration file, then 10 minutes later (but before you check in) I create one. My database version will be timestamped to when I created mine, which will be greater than your migration. So after you check in, I would still need to know to downgrade my database prior to your migration, then run both migrations.
jswanner: From the linked article about the enhanced migrations plugin;
“The biggest difference from the standard way is that migrations below the current schema version are still applied if they have not been applied yet (not in the tracking table).”
In response to those wary of the new system:
http://en.wikipedia.org/wiki/Addition#Commutativity
The fundamental model behind migrations is one where a linear “diffs” can can be applied to a database in chronological order to make the final product.
The previous system of numbered versions was insufficient, because if commutative diffs were made, they had to be manually merged or modified to enable them to be applied correctly.
Timestamp based migrations were built to solve that one particular problem: when 2+ developers make commutative diffs, where previously they would have to me merged or modified, now they Just Work™.
It doesn’t solve the problem of non-commutative changes, though, but it doesn’t intend to. Those must still be merged or modified manually.
It is possible to minimize the amount of manual intervention by declaring and building dependency graphs between migrations. You could even use rake, which has dependency calculation built-in, but that is “too much software” and sounds way too enterprisey. If you were an economist, you might say that the cost (lots more infrastructure code + more code required for each migration to describe dependencies) outweighs the benefits (a very small percentage of new diffs wouldn’t require manual merging or modification).
So, welcome the new changes, but don’t be fooled into thinking it’s a panacea. As well, don’t criticize it because it doesn’t solve every single possible case of problem. Nothing is magic.
Oh… I get it now :) This was previously available in the plugin enhanced migrations, and now it’s in edge rails! I’ll have to check have my eyeballs checked.
Tom: UTC based migrations do look ugly, but in practice they haven’t been difficult to deal with. db:migrate:previous still works, and migrating to a specific UTC stamps works as expected as well. We branch like crazy at my company, and do a lot of parallel development. Incremental migration numbers were a nightmare for us, because whenever we merged two branches, or did a release and merged back into the task branches, keeping our migrations revisioned was just pure insanity (we were considering having a special migrations branch, where all migrations were created/modified from there… but that was too much overhead).
As the current maintainer of the Enhanced Migrations plugin, I’m glad to see it get such exposure outside of our own setup. We’ve been using it in all our projects for over a year now to great success. It tackles very effectively the problem it was designed to solve, namely conflicting database migrations in a large development environment.
FYI, we updated the plugin in November (ver 1.2.1) to address some minor issues. A new version should be forthcoming in the next few weeks as well. You can find this and other plugins developed at Revolution Health at our Rubyforge site. Feedback is welcome.
http://rubyforge.org/projects/revhealth
I just took a quick look at the changeset. It does not seem to work exactly like enhanced_migrations, which keeps a table of the run migrations, instead of just the last version like the current schema_info. That means on branch merge, I will have to manually run migrations from the branch where the timestamp is smaller than the current version.
Is that correct? If it is I’ll have to keep using the plugin.
On which version didi you test it?
I am thrilled to see this change. Perhaps we should have been using the Enhanced Migrations plugin but we have a case where we need to merge in a set of migrations into each of our new projects. We keep running into problems were we have two migrations with 001_. This solves our problem.
After you {git, svn} merge and new migrations appear in the middle, you have to ls to find which ones are new, and copy & paste to rake db:migrate:up VERSION=foo to migrate the new ones.
Or you can help me test this patch (http://dev.rubyonrails.org/ticket/11493), +1 it, and then in Rails 2.1 you can just do rake db:migrate and it’ll pick up the new ones. Thanks!
I don’t see much improvement changing to UTC based migrations.Let’s say I created migration at 03:15 pm today and didn’t commit it to the repository yet because im not done with whatever Im working on,well…5 minutes lter my partner does the same but he ’s working on a different part of the project.Well,When I commit my changes to the repository and he updates it,the migrations won’t work because the schema_info now records the last migrations timestamp he used,so it will ignore my migration cause it was created before my partner’s migration file.
When converting to edge, do we need to change our current migration naming, then?
So when moving to edge, do we need to change the naming scheme of our current migrations setup?
http://dev.rubyonrails.org/ticket/11493
It appears the patch was accepted and is now in Edge Rails. That means that in Rails 2.1 it will track each migration that has been run, and when migrating up will run ones that haven’t yet been run. When migrating down it will only revert ones that have actually been run, skipping ones that were never run on that DB.
Excellent!
Ryan, perhaps this warrants an update to your article?
Great idea, Peter – done.
So, instead of rolling back by typing up to a 3 (or-
unlikely-4) digit number, I now get to type a FOURTEEN DIGIT NUMBER?! Oh, yeah, that is time-saving!</sarcasm>rake db:migrate VERSION=54 rake db:migrate VERSION=20080609142333
Yes, definitely easier.
Which makes sense to use if the target version is not too far away. Want to run that command 10 or 20 times?
Oh, and when all the migrations are listed out in my IDE (Eclipse), they are sorted by filename. With the recent “improvement,” my list can now be sorted out-of-order, making it just that much more confusing. Taking jswanner’s scenario above (April 2), the list of migrations is immediately out of order—and there’s no way to order them properly, short of renaming the file(s) manually.
Is there a better solution? Not sure, but it would’ve been nice if there was a way to turn this one off and use the “classic” methods. I’ve easily created hundreds of migrations, branched, and merged without much difficulty on teams of one to many.
It’s still not too late to undo it. Please.
It is a crazy idea. I am now renaming migrations by hand! Surely we should be able to configure this somehow?
I think the new system is an improvement, but we were using enhanced_migrations and now we’re having issues with it trying to re-run those migrations. Gotta figure that out.
Hi, i decided to rename my old migrations files with the new convention, is there any script to make this task easy?In case not, which is the best way to rename them by hand?
Thank you very much!
If the format of the name is yyyymmddhhmmss_name, then how can the files be listed out-of-order? Each subsquent number is a subset of the previous number.
Year>Month>Day>Hours>Minutes>Seconds
I’m also using Aptanta (Eclipse) and RadRails Plug-in, and the files are displayed in the correct order.
hello thank you , useful post
when i use migration for addin additional column i cant see the result on the app itself but i can see this column in my table on database
can you help me please
WTF is a Migration?
I am using eclipse and it does not let me run the new migrations, I get “Invalid Migration Number”, running directly from rails works fine. Anyone know what the issue is? I am running version 3.4.1 which is not that old.
Jeff, I had the same problem. I found that to get around it (while still using “Run Migration” from RadRails) I had to old-school-ize the migration name back to 001_whatever.