Join our team!

We're looking for mid-level and senior software developers who love working throughout the stack, and have a track record for designing, building, shipping, and supporting web services and applications. Ideal candidates love Ruby, Rails and Ember, and using BDD and agile processes to ship working software quickly! Experience with Redis, Postgres, RabbitMQ, and ElasticSearch are a huge plus!

Learn more about our mission by visiting IoraHealth.com and by following @IoraHealth.

Saving Time with CI

Have you ever lost your groove waiting for a build to complete? You are coding up a storm, being extremely productive, and then you need to run all your tests before you merge into master. BOOM, productivity destroyed!

Compiling (XKCD comig)

This was becoming a big problem on our ICIS project. Our full test suite started taking over 20 minutes to run (it is now up to 30 minutes). Something had to be done.

We looked at a couple of ways that we could speed up our tests:

  • Use a headless browser, like capybara-webkit, with our Cucmber features.

  • Use Jasmine to BDD the front end code (our app’s front end is written in Backbone.js). This will reduce the steps we need to cuke.

Although Jasmine helped a lot we could never get capybara-webkit to work for all our features and running a headless browser and firefox took as long as just Firefox.

Even by speeding up our test suite there is only so far that could take us. Plus as we BDD we add more and more tests.

Our Initial Development Process

We are an agile Test Driven Development shop. Our initial approach to development was as follows:

  1. Select the top story in our backlog.
  2. Fetch the latest version of master.
  3. Create a feature branch from master.
  4. TDD the feature.
  5. Get new tests to green.
  6. Run the test suite and get all tests to green.
  7. Create a pull request.
  8. Upon acceptance merge into master and push to origin.

When this goes smoothly we would spend about 20 mins for our tests to pass. When we had lots of problems it might make two to three times that.

Leverage CI

Our solution was to delegate testing of the full test suite to our CI server. We use TeamCity for our CI and it is set up with three build agents, allowing three concurrent builds to execute.

TeamCity provides a neat way of creating project templates. So we set up a template that is dependent on the branch name. The project template is set up to poll Github for updates to its branch and kick off a new build if there are any. Then we created the following projects from that template: master, wm-ci, pr-ci, jm-ci, jn-ci, bf-ci, ms-ci.

So now when someone pushes an update to any of those branches on Github a new build will be kicked off. Each branch ending in ci belongs to a particular developer on our team, e.g. wm-ci belongs to me.

Our Current Development Process

Now that each developer and designer has their own personal ci branch we can utilize this to keep down the time we spend waiting for our test suite to run. Our approach to development now becomes:

  1. Select the top story in our backlog.
  2. Fetch the latest version of master.
  3. Create a feature branch from master.
  4. TDD the feature.
  5. Get new tests to green.
  6. Run the test suite and get all tests to green.
  7. Push to personal ci branch and wait for green before going to step 9.
  8. Go to step 1.
  9. Create a pull request.
  10. Upon acceptance merge into master and push to origin.

There is only a small difference to our process but it allows us to continue working while the tests are running. We have also hooked up TeamCity to post build status to Campfire. So when a personal build is successful the developer can create the pull request and continue to work on there second feature. If the build failed, the developer can decide whether to continue their current feature or to fix that first one.

To make things a little simpler we have added the following to our ~/.gitconfig files:

[alias]cibuild = push origin HEAD:wm-ci -f

So now when I want to kick off a build of my current branch I simply run

git cibuild

Conclusion

We find this approach has been working really well for us. We do sometimes encounter a bottleneck where we have many features in the CI queue (but there is always more features that can be started).

We run our CI server on EC2 and our two additional build agents get launched when needed. And remember EC2 instances are a lot cheeper per hour than developers.

I recently gave a lightning talk at Boston Ruby on this subject. The slides and video will be available here.