I’ve mentioned Rails migrations in the past. They provide a way to script database schema changes so that you can keep them in version control and roll back versions of a schema if necessary. One thing about them though is that eventually your list of migrations gets huge. (We’re up to over 20 of them in some of our applications, and there’s no reason why we might not have over 100 some day.)
The question becomes, when do you delete all of your migrations and start over with a fresh schema? It seems to me that eventually it comes time to create a fresh database, run all of your migrations on it, and then export that database to a new file and then delete all of your migrations and call that new file migration #1.
My feeling is that this is a function of application versioning. When you reach a big milestone (the kind you wouldn’t want to roll back from incrementally), it’s time to bundle up all your migrations. Unfortunately, that seems like it would conflict with maintaining an existing application. The schema version is stored in the database for live applications, and if you reset the number you lose the ability to run subsequent migrations against an existing database.
Anyone know if there’s a plan for how to handle this sort of thing in the Rails world that I’m missing? The reason I ask is that we have applications that we’re planning on releasing as open source. They also have live instances that are currently running. For the sake of those applications, it seems logical to never roll up the migrations. Distributing an open source application that has to run 50 migrations to get the database set up seems like a bad plan, though. You could distribute your application with a schema import file, but then when you release subsequent versions, you lose the ability to distribute a migration with them that automatically updates the user’s database.
I guess I need to do some research.
Nested loops, or when to write a method
I was trying to fix a bug a few days ago and my initial fix involved nested loops. The code pulled some random values out of a list and added them to another list (creating wrong answers for a multiple choice question). I of course forgot to check for duplicate values, introducing a bug. The humbling thing about software development is that I still routinely make mistakes like this after ten years of getting paid to write software.
Anyway, I went to fix the bug, and so I just put another loop inside the loop that added the answers to the list which made sure that the value I picked wasn’t already assigned to an answer I had added. If the value was already used, I just did this:
continue;
Guess whose fix didn’t work? This statement was applied to the inner loop that checked for duplicates rather than the outer loop that added answers to the list and the duplicate answer kept getting added as before. The next logical step was to use a label, so I could label the outer loop with something helpful like
outer
and then change the statement above to:continue outer;
This would work, but I don’t think it would add much in the way of clarity. Instead I added a method called
hasAnswerWithValue()
to myQuestion
class and replaced the inner loop with:if (question.hasAnswerWithValue(newAnswer)) { continue; }
I may even use the new method somewhere else at some point. In some cases, there’s no way to avoid the use of nested loops, but I’ve come to think that you should always do so whenever it’s practical.
In fact, I’m coming to think that “minimize the number of loops and conditional statements” is a good principle for programmers. That’s another post though, because that’s not what I did in this case. I just hid the loop in a method instead of nesting it.