Improving software delivery in every organisation

Continuous Integration and Continuous Deployment

Over the course of development, software changes continuously and as software changes bugs & misfeatures will arise. In order to ensure feedback is as relevant as possible continuous deployment was introduced.

Continuous integration is a way of working where developers reintegrate their code into the upstream repository as frequently as possible. Continuous Deployment takes this one step further and automatically builds, tests and deploys applications so long as the steps complete successfully.


If you have built this project with other people, pair or mob with them through this guide.

This guide assumes you are already familiar with Git, and the concept of Pull Requests.

In this guide we assume that you have locally configured your shared git remote to be named origin.

You will need a project hosted on GitHub (preferably open-sourced, and without an automated build tool configured for it) and accounts on Heroku and CircleCI.

You will need to install the Heroku command line client.

Automating Tests

We’re going to use CircleCI to automate some tests.

First, you’ll need to pick a project to automate tests on.

If you are using RSpec, all you need to know is your Ruby version for the docker image section (use ruby -v on the command line), and then your config.yml file can be as simple as:

version: 2.1
  ruby: circleci/ruby@0.1.2 

      - image: circleci/ruby:2.6.3-stretch-node
    executor: ruby/default
      - checkout
      - ruby/bundle-install
      - run: bundle exec rspec

If you are using a Ruby project, note that this config should be very similar to the pre-populated config.yml file you got in the Setting up CircleCI step of the instructions linked above.

Note that the above config.yml assumes you have a Gemfile - which can be as simple as this one. You’ll also need a Gemfile.lock later on to get Heroku working - you can create one by running bundle install after you’ve added your Gemfile.

If your GitHub project is at, your CircleCI build will be at

  • Trigger a build on CircleCI. Does it pass?
  • Does it run tests if you push to your main branch in GitHub?
  • Does it run tests if you open a pull request? Note that you will only be dealing with pull requests if you are using branches in your branching strategy - see Git Branching below.
    • To open a pull request:
    • Create a branch (git checkout -b branch_name)
    • Commit some code
    • Push the branch (it will prompt you to run git push --set-upstream origin branch_name first)
    • At GitHub, go to your repo and click Pull Requests at the top
    • Click Compare & pull request next to “branch_name had recent pushes n minutes ago” (If you don’t see that, you might need to select your branch from the dropdown on the right).
    • Enter a description, and click to create the pull request
    • You should see it running tests in CircleCI
    • You can now click Merge pull request to get your changes merged into the main branch.
  • What happens if you open a pull request from a branch with failing tests? Can you still merge the request? What changes?

Automating Deployment

Using CircleCI and Heroku you can configure your application to be automatically deployed to the web. However, note that if your app has a command-line interface (rather than a web interface), then you don’t gain much from Heroku deployment. In that case I would recommend you create a new repo with a simple Sinatra app as described at the beginning of Learn Enough Ruby here, or you can fork this simple Sinatra app.

Follow the Creating an application on Heroku, Configuring Heroku access on CircleCI and Adding the deploy configuration sections here to deploy your app to Heroku. Note that the config.yml file referenced in the Adding the deploy configuration section is the same one that you created in the Automating Tests step above, so you can just add to the existing file.

### Things to think about

  • Does the app still deploy if the tests fail? If so, see the requires step in the heroku_deploy workflow here to make a step dependent on a previous step.
  • If you are using branches, are changes to all branches deployed? If so, is this desirable? See the filters step in the heroku_deploy workflow here to only deploy for a specific branch.
  • An alternative way to deploy to Heroku is that you could remove the heroku/deploy-via-git job from the CircleCI config.yml and try configuring the deployment through Heroku by integrating with GitHub directly. Note in particular the Enabling GitHub integration section and the Wait for CI to pass before deploy checkbox detailed in the Automatic deploys section.


  • Use the CircleCI language docs and the CircleCI deployment docs to help you identify problems.
  • If you’re on Windows and you get the error “Your bundle only supports platforms [“x86-mingw32”]”, then replace x86-mingw32 with ruby in the PLATFORMS section in Gemfile.lock.
  • If you get errors about Bundler versions, add the following to config.yml on the line before the ruby/bundle-install step (explanation here - note that this explanation is in reference to travis, but it’s the same principle):
  - run: gem install bundler
  • If you’re using a Rack-based framework such as Sinatra and are experiencing issues when deploying to Heroku, this article about the file may be helpful.

Git Branching

Have a read through of Martin Fowler’s article on the various approaches to branching to get an understanding of the pros and cons of each approach. It’s a long article though, so you might want to jump straight to the section on continuous integration.

At Made Tech we practice Continuous Integration, as we have come to the agreement that the pros of this approach outweigh the cons.

  • Why do you think that is?
  • Discuss scenarios where Continuous Integration wouldn’t be appropriate

The Trade-off

Continuous integration is a branching style with more stringent rules than others and so requires a level of commitment from developers in order to do its job.

Some Rules

  • origin/master should pass its tests at all times. All commits in master should compile and pass all tests.
  • origin/master should be deployable at all times. All commits in master should be of sufficient quality to be deployed.
  • Where necessary, release should be decoupled from deploy. It should be possible to deploy master, and turn on new features independently. For instance the latest deployment contains a new feature, but your users can’t actually see or use that feature yet - ie it hasn’t been released. This approach is commonly referred to as “feature toggling” or “feature flagging”.
  • Pull requests should be short lived (~4 working hours maximum), and short (~400 lines of code maximum). Pull requests should be simple to review.
  • Pull requests should pass all tests, and not have any linter warnings (where appropriate). Pull requests should be tested before merging.

Some Practices

  • Build new functionality in small increments, by employing a good software architecture and a comprehensive suite of tests.
  • Use an automated build tool (e.g. Jenkins, Travis, Gitlab CI, CircleCI, etc) to run tests every time a new commit is pushed to origin/master
  • Use an automated build tool to run tests every time a new commit is pushed to a Pull Request branch, and also run the tests as an “Integration” with master. (More on this later)
  • Use your automated build tool to deploy successful builds

Feature Branches

If you do plan on using feature branches, you might want to refresh your understanding of git merges. Make sure that you can:

  • Draw out a simple Git branch with commits that apply on top of each other.
  • Draw out what happens when a Git branch is merged with two parent commits.
  • Draw out what happens when a Git branch is rebased ontop of a branch with commits.
  • Explain what HEAD, master and origin/master refer to.

Speak to your mentor if you need a refresher.