Continuous deployment is...

No, let's take a step back and see what the differences are between continuous integration, continuous delivery and continuous deployment.

Continuous integration

Continuous integration is the process of merging development work with the master several times a day / constantly. This helps:

  • catch issues early
  • prevent "integration hell"

Most of the work here is done by automated tests.

Continuous delivery

Continuous delivery is the practice of delivery of code to an environment, whether it is a QA team or customers, so they can review it. After the changes get approved, they can land in production.

Continuous deployment

You can think of continuous deployment as the next step of continuous delivery, when each change that passes the automated tests are deployed to production automatically. Continuous deployment heavily relies on an infrastructure that automates and instruments the process of testing, integration and deployment of new features.

In this post we will go through these automation steps and cover most of the principles.

A simplified continuous deployment flow can look like this:

continuous deployment flow

From Source Control to Production

Let's take a scenario, when a new feature is going to be developed and we want to see that in production. We will take a look at the life cycle of a code changeset from a commit till it goes live on our production environment.

It all starts with a commit

Every commit to the master should trigger a new build with tests - but when adding new functionalities, you do not want to see that half-ready feature in production.

Feature toggles

To solve this issue, continuous deployment setups usually go with feature toggles. Feature toggles are alternatives to feature branches and allows developers to release a version of a product that has unfinished features. These unfinished features are hidden by the toggles in production environment.

// dummy example to show a feature toggle using
// https://www.npmjs.org/package/feature-toggles

var featureToggles = require('feature-toggles');  
// define toggles
var toggles = {  
    foo: true, 
    bar: false
};

// load them into the module
featureToggles.load(toggles);

// check if a feature is enabled
if (featureToggles.isFeatureEnabled('foo')) {  
    // do something
}

When the feature is ready, the feature toggle can be removed.

Continuous deployment tools

But where does it trigger a new build? For this, you will need a continuous integration tool. There are a lot of them out there, including Jenkins, Travis, Codeship and Strider, which is written in Node.js. Jenkins and Strider are open-sourced, and can be operated on your own infrastructure.

Currently, we use Strider for our closed-source projects, and Travis for our open-source projects.

Each one of these tools support commit-hooks, so set one up! In this case your continuous integration tool does not need to poll git/svn regularly.

Build on commit

After the tool of your choice gets notified of a new commit, it starts a new build. A build can have a lot of steps, some of them can run in parallel. Speaking of Node.js applications, the following steps can occur:

  • installing dependencies from NPM (public or private)
  • run unit tests
  • build assets, like css and javascript
  • run integration/end-to-end tests
  • creating artifacts (bundle the node_modules directory to it as well, so during deployment, you won't depend on NPM)
Automated tests

Automated tests are the most crucial parts of the build process.

Your modules must be covered by unit tests, and to check if everything works together you should have integration tests in place too. For these types of tests you can use mocha/tap/Jasmine, and an expectation library like chai.

Depending on whether you are creating an application with a frontend, or just an API, you can choose different tools for end-to-end testing.

If your application does not have a frontend, but is an API, you can use hippie or supertest for end-to-end tests.

When developing an application with frontend involved, you still have options to test the user interface as well. Protractor for AngularJS applications or Nightwatch. To make sure it works in every browser you support, run your end-to-end tests on a Selenium cluster. Or you can use services like Sauce Labs or Browserstack.

I cannot stress this enough: without good test coverage, Continuous Deployment can lead to serious production issues!

Creating artifacts

If all the tests pass then it is time to create an artifact from the build. An artifact should contain every single file that is necessary to run your application, so your production servers won't have to deal with building it again.

A simple tar filename.tar * can do the trick. Then make sure to place this file in a location where it is accessible for your production servers, so they can get it, like Amazon's S3, or any other storage.

Deploy

As we just created an artifact containing every assets that our application needs, we only need to do the following things:

  • download the latest artifact
  • unpack it to a new directory
  • update the symlink, so it will point to the directory just created -
  • restart the node application

It goes without saying: this process must be automated, and no manual steps should be involved. Tools like Ansible, Chef or Puppet can help.

Rollbacks

If things can go wrong, they will. Make sure to have a rollback script in place. The fastest and easiest way to do this is to set the symlink to a previous build and restart the node application.

Recommended reading:

Operational tips on how to run a Node.js infrastructure.