Sinatra/Heroku microservices

I spent some of this weekend working on a side project. It began as a monolithic Sinatra app, then became two apps, and then finally the design settled on five microservices. At some point in this evolutionary journey I had a separate git repository for each microservice. But some of them wanted to share code, so I also had yet another git repository for each shared library, with the shared code included via git submodules.

This was a nightmare to work with, particularly as all of these git repositories were evolving simultaneously. So I wondered whether I could have a single git repository containing the code for all five microservices. But then how could I deploy them as separate Heroku apps? The answer lies in this Heroku article about managing multiple environments:

For simplicity, imagine we have a single git repository containing two Sinatra apps, stored in app_a.rb and app_b.rb. First, create a heroku app for each:

$ heroku create --remote app_a
$ heroku create --remote app_b

Now set an environment variable in each, telling it which of the two apps it is:

$ heroku config:set WHICH_APP=app_a --remote app_a
$ heroku config:set WHICH_APP=app_b --remote app_b

And finally in I configure Rack to start the app that matches the environment variable in each instance:

require 'rubygems'

service = ENV['WHICH_APP']

if ['app_a', 'app_b'].include?(service)
  require File.join(File.dirname(__FILE__), service
  run Sinatra::Application
  abort "Unknown microservice '#{service}'"

Now, when I push to Heroku:

$ git push app_a master
$ git push app_b master

each Heroku app will launch a different Sinatra app.