Facebook Application Logistics for Team Development


Facebook is a unique platform for application development -- Facebook applications have a powerful API, a large user base, and low barrier to entry. With the Rails Facebooker plugin, a Rails programmer can treat the Facebook user data as an extension of existing Rails session features. It's all very nice, and I'm not going to talk about any of that this time around.

I'm going to talk about the logistics of doing Facebook apps. When a user sends a request to a Facebook app, Facebook intercepts it and forwards the request to the third-party app server, along with some specific Facebook data, like the ID of the user making the request. Applications have to be registered with Facebook, and have a unique URL prefix at apps.facebook.com. You also provide the URL for your third-party server.

This sets up some challenges for a development team. First off, your application probably relies on Facebook services, meaning it can only be fully tested from within Facebook. This implies that the external Facebook server needs to be connected to your development server. Since many Rails developers run off their own development machine behind a firewall, this is potentially awkward. Similarly, the fact that the Facebook application has a single URL mapping makes it difficult for multiple developers to work on the same application without tripping over each others' work.

How can a development team make this work?

The Facebooker plugin offers a service to create an SSH port tunnel from the open URL and port you designate on your development laptop. This works, although depending on your network setup, you may have some firewall hoops to jump through. Furthermore, your IT or networking department may have qualms about letting a tunnel from Facebook through to your developer subnet.

So, the question becomes how to put the developers work on some kind of staging server without putting too much friction in the system from the developer's perspective. I'm not sure that we've completely licked the problem, but the solution we're starting with seems reasonable.

What we've done is set up a staging server with a series of open ports. Each developer works on the program locally, and we've set up a fast mini-deploy from the developer's machine to the staging server for testing.

Each developer sets up a public-key SSH connection with the staging machine and signs up for a specific port. Within Facebook, each developer sets up a scratch application for use on whatever project they happen to be working on and points that application at the staging machine and that developer's port.

Moving files back and forth between the developer and staging severs is managed by a series of Rake tasks. Each developer sets up a staging.yml file in their config directory:

remote_username: nrappin
remote_host: x.x.x.x
remote_directory: staging_app
remote_port: xxxx

The main rake task is a simple rsync call to the staging server based on the parameters in the YAML file:

require 'yaml'

namespace :remote do

def ssh_host

task :config do
staging_yaml = File.dirname(__FILE__) + '/../../../../config/pathf_staging.yml'
config = YAML.load_file(staging_yaml)
@remote_username = config['remote_username']
@remote_host = config['remote_host']
@remote_directory = config['remote_directory']
@remote_port = config['remote_port']

task :rsync => :config do
sh "rsync -az . #{ssh_host}:#{@remote_directory}"

With that in place, the command rake remote:rsync will transfer the entire app to the staging server.

All kinds of single commands can be run remotely -- this batch opens an SSH shell, runs database migrations, and then mongrel starting, stopping and restarting.

task :ssh => :config do
sh "ssh #{ssh_host}"

task :db_migrate => :config do
sh "ssh #{ssh_host} 'cd #{@remote_directory}; rake db:migrate'"

task :mongrel_start => :config do
sh "ssh #{ssh_host} 'cd #{@remote_directory}; mongrel_rails start -d -p #{@remote_port} -e staging'"

task :mongrel_stop => :config do
sh "ssh #{ssh_host} 'cd #{@remote_directory}; mongrel_rails stop'"

task :mongrel_restart => :config do
sh "ssh #{ssh_host} 'cd #{@remote_directory}; mongrel_rails restart'"

Rake lets you combine these into some useful combinations

task :deploy => [:rsync, :mongrel_restart]
task :start => [:rsync, :mongrel_start]

That's a reasonably easy deploy to the staging server (which doesn't stop me from forgetting to do it now and then). You can make it even easier by binding it to a key command in your text editor, or if you are feeling really ambitions you can bind it to fire anytime a file in your application is saved (the way autotest works).

This setup lets each developer work in their own sandbox with only a minimal amount of deployment overhead. When the time comes to go to production, the application is registered for real with Facebook, and gets its final API key and URL information.

If you liked this, you might also enjoy my book Professional Ruby on Rails.