Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

Monday, October 26, 2009

Tips for using Rails migrations

 The concept of a migration is that each change that is made to the structure of the database is captured in a version controlled script.  Any time you want to create a table, add a field, change a field type, etc., this is done via a migration.  Migrations are generated along with models, views, and controllers when a Rails scaffold is created to manage a resource. In addition, migrations are used to create a test database for each user which allows for rapid, automated quality checks on the application. If you have ever worked on a project where it is difficult to create a development "sandbox" database for each developer, where databases are copied via backup processes that leave weird permissions behind (I'm looking at you SQL Server), or where keeping the development database in sync with the application is something you even have to think about, you will easily see benefits from migrations.

Using Rails migrations can be a bit of change for teams. In some cases, developers are not very familiar with relational database concepts and object modeling principles and can create some overly complex and/or inefficient structures. Having the overall model reviewed by a database modeling expert and an object modeling expert can be quite helpful.  I recommend Coad's "Java Modeling in Color with UML", Fowler's "Analysis Patterns" and David Hay's "Data Model Patterns"  for getting yourself up to speed with this. However, it is also worth communicating with the rest of the team that may be touching the model. 

Here are a few things we've done in the process of keeping migrations under control:

Don't edit an existing migration, create a new one.
     Editing an existing migration defeats the whole point.  You might be able to get away with it if no one has run it.  One obvious exception to this is if someone creates a migration that doesn't run and checked in. They must get the pig.


Update and run new migrations before you check in a migration.
      While this might seem obvious, in many cases, it is easy to check in a new migration file to the repository without seeing if someone else has created one that does something that conflicts with yours, since the names will be different.  If you don't run the migration, you risk doing something with your migration that causes issues. If you have already run your own migration locally, but not checked it in, and you get an update of a new migration, you need to roll back your own migration and change the numbering on it so that it runs after the migrations you just received from your version control system.

Check in the models that go with the migration when you check in the migration
     One key benefit of migrations is keeping the code changes in sync with the database changes.  Add new tables and models in a single, logical commit.


Assign models to class owners
      The feature of XP that I have been least successful with is the concept of common code ownership.  It is one of the easiest practices to apply in theory, but without the sense of collective responsibility and the compensating practices of pair programming and continuous integration, it can be problematic.  In larger teams, it often makes more sense to have people or subteams assigned to manage a particular model so that changes can be coordinated.  In theory, there can be enough communication to make it work. In reality, many teams are full of introverts that would rather rewrite huge swaths of code (and tests) than ask a question.

Communicate
     Changes to the object model can impact the whole application. These changes should be communicated to the rest of the team so that the reasoning behind them can be better understood and the data can be used correctly.  There is a risk of someone criticizing or trying to change your proposal, but this is ultimately a lower risk than building something that others fail to understand.

Roll up all migrations into a big file after a major release
    Once you've been going in your project for a while and get around the 100-150 migration point, it is probably worth it to roll the migrations prior to the current point into a single migration.

Generate SQL to run on the production database
   In many cases it is preferable to generate SQL to run on the production database, as opposed to running the migrate task directly against the database. This also allows for some more extensive code review on structural changes.

Don't forget to create constraints and indexes (indices?)
  If you are using foreign keys in your database, it's often worth the cost to create a constraint on the column to prevent any data integrity issues.  It's also a good practice to create an index on those columns to speed joins.

This is really just the tip of the iceberg, but it's a yummy water ice iceberg. Learning to use database migrations is a key skill, not just for Rails development, but for any agile development. A project which cannot create its database from scripts accessible to every developer is missing something important. By breaking those scripts down into small chunks and coordinating them with changes to the code, the situation where the application and database are out of sync simply does not exist.

Thursday, September 10, 2009

Default route...

One of the things that has been bothering me in Rails since everything switched to resources is how we lost the easy way any public method in the controller became an accessible URL without having to write a rule for it. For example, in a person controller, I could define a search method like this:

def search     
results = Person.seach(params[:q])
end
And it would be accessible, just like that. http://myapp/people/search?q=matt
I even wrote a simple router in Java to do the same thing, because it just makes it so easy to navigate the application code.

The poor design of map.resources messes this up. By adding the route pattern :controller/:id it starts with the default assumption that the search?q=matt part of my URL is an ID. Is it so hard to put an action name in there? In any case,
map.connect ':controller/:action'
goes above all of the map.resources calls in my app. You might want to put it in your application too.

Tuesday, September 23, 2008

Rolling Restarts, Migrations, and Deployments


I was looking into a question on stackoverflow (sweet site!) and it brought to mind the awesomeness and challenges of dealing with restarting a load balanced array of servers. The awesome part is you can do a deployment with any downtime by taking down a server, updating it, bringing it back up, and moving on to the next one. One issue with this is that you end up having multiple versions of the code live simultaneously. This can get a little weird if a user hits a new view page then the request ends up being loadbalanced to a server that isn't up to date yet.

The solution we came up with (before we heard about SeeSaw) was to take half of the mongrels off line from the load balancer. Shut them down. Update them. Start them up. Put those mongrels back online in the load balancer and take the other half off. Shut the second half down. Update the second half. Start them up. This greatly minimizes the time where you have two different versions of the application running simultaneously. I wrote a windows bat file to do this. (Deploying on Windows is not recommended, btw)

A truly awesome solution to this would be a load balancer that is somehow aware of the version level of the balanced set and just makes the switch for you. Until that is invented, Apache mod_proxy_balancer is easy enough to control remotely.

It is very important to note that having database migrations can make the whole approach a little dangerous. If you have only additive migrations, you can run those at any time before the deployment. If you are removing columns, you need to do it after the deployment. If you are renaming columns, it is better to split it into a create a new column and copy data into it migration to run before deployment and a separate script to remove the old column after deployment. In fact, it may be dangerous to use your regular migrations on a production database in general if you don't make a specific effort to organize them. All of this points to making more frequent deliveries so each update is lower risk and less complex, but that's a subject for another response.

Friday, July 11, 2008

JRuby- The Element of Surprise

JRuby...I used to think, "meh, who needs Java? I'll stick with the c implementation." Then I started working on a project investing millions of dollars in infrastructure to run Java web applications. Sadly, I initially agreed with the assessment that it was a bit of a hack to run the Ruby on the JVM. Thus I soldiered on, avoiding JSF and EJB, but getting sucked into many of the other indignities of the Java world.  The opportunity arose to build a quick prototype for a different system. The desire to go the Rails route proved successful with a quick delivery and excellent customer feedback- but how would we ever deploy it? How would the proprietary Sun Single Sign On service work? Could it possibly connect to Access Manager- a feat that had taken weeks of development time in Java? A couple of emails with Arun Gupta and Nick Sieger led me to some of the truly impressive work that has been going on. Warbler builds a war file out of your Rails app using jruby-rack. Drop your .jar libraries in the rails lib dir. Muck with your web.xml so you can make it use a filter. Drop the .war file on your server. Done. A little method called servlet_request is now magically available to you. Call servlet_request.getUserPrincipal : it's populated. It's not magic- it's JRuby!
  

It's really suprising how much faster things seem to get done. Now, I know that's sort of silly. People can work fast in any technology that they are good at. In fact, I think the source of most of the disagreement in technology and product selection (which is a plague in the Java world, the GIS world, the database world, etc.) is that people want to use what they are best at because it allows them to shine. I ultimately only care about the end result. I don't mind switching to some technology I know nothing about. It is harder for me to provide value, but I love nothing more than learning new things and evaluating them. 

Anyway, that's why JRuby is even more awesome. Let them write Java. The JVM is a environment where we can all get aong.

Tuesday, December 25, 2007

Ron Jeffries Can't Get Rails Running


This is kind of a strange story, but why the hell can't he get it running? This is when to call in VMWare and have a nice set of clean machines available. Apparently he was running SlimServer, which sounds slightly immoral so I am sure he deserved the non-functioning environment. Anyway, nice thing about XP is early cancellation, but, he could have just asked for help...

That said- development environment set up is not that easy. I had one J2EE project where we had a two day process. For my last Rails project, it was a about two pages on the wiki, most of it related to SQL Server driver (don't ask) and Ferret. It did remind me to not depend on the availability of external resources for the install procedures though- what's out there on the Rails Gem servers can change.

What's killing everyone on one of my internal corporate projects is svn+ssh. That said, our most recent hire nailed it pretty well, even though it was his first time using a Mac and NetBeans, and I am using RadRails on Windows.

Anyway- it's worth writing up the procedures for the development environment set up, and then having the new person update those when they come on board as their first task. Of course, if no one has started for a while, chances are the instructions just won't work.

Wednesday, July 18, 2007

A dozen readings later...

I guess I am finally reaching the "advanced" Rails tricks, since I just used this. (Although, I have to admit the origin was a ryanb tutorial on editing multiple models, not from repeated readings...)

from Agile Web Development with Rails

An Extension to Ruby Symbols

(This section describes an advanced feature of Ruby and can be safely skipped on the first dozen or so readings....)

We often use iterators where all the block does is invoke a method on its argument. We did this in our earlier group_by and index_by examples.

groups = posts.group_by {|post| post.author_id}

Rails has a shorthand notation for this. We could have written this code as

groups = posts.group_by(&:author_id)

Thursday, December 28, 2006

Zed Shaw


Zed is king of the internet...for now.

If anyone out there is using this newfangled Ruby on Rails framework, Zed's (open source) Mongrel product has become the de facto container for running your app. It's a tiny little c program, and dead fast and stable. He co-wrote a little PDF on it that I had printed on a pillowcase. It's so comforting to know that my apps are running on Mongrel that the pillowcase serves as a little reminder so that I can go to sleep- without a glass of wine and one of those little blue capsules.

For a more general interest look at why Zed is such an interesting character, check out his essay blasting Joel Spolsky and Don Norman's articles on simplicity vs. complexity. If you aren't following that "debate", good for you...I'll summarize though- some people think simplicity is good. Some people think complexity is good. The people that are right think that good design makes complex tasks as simpler as they can be, but no simpler.

Wednesday, October 04, 2006

Rails for Java Developers- beta book


I just started reading the early beta of Rails for Java Developers. It's a new book published by the Pragmatic Programmers and written by the Relevance guys- Stu and Justin. Perhaps the most shocking thing is that they have time to write a book. I have been lightly involved in the review process for O'Reilly's upcoming "Rails Cookbook", currently available as a Rough Cut. It's a lot of work to stay current with a moving target and make sure all of the code works.

As far as the book goes, I think it really is going to fill a gap in terms of being a reference for people that know how to do something in Java, and want to know what the equivalent thing is in Ruby on Rails. It's like those options in a new version of a product to see the menus for the prior version, so that you know where to find things. Of course, I haven't tried much in the book yet, so I can't speak to it's quality, but I like the tone and the approach so far. It appears to be just over half way to their 300 page target, so, there's a lot to do before Nov.