Continuous delivery is a broad set of software development practices designed to ensure that every change is rapidly deployable to production, while the system performs its business function uninterrupted and as intended.
Continuous deployment is a particular instance of continuous delivery in which all source-code changes that pass an automated test suite are automatically deployed to production environment. By automating the repetitive work while establishing good collaboration, development and operational practices, continuous deployment can enable and sustain high productivity in your team. In this article, we will discuss the practical elements of one possible continuous deployment workflow, based on our experiences at Semaphore.
Always Start with a New Feature Branch
We’ll assume that you’re using Git for version control, although the same applies to any other version control system that is optimized for distributed work. Starting from the main branch (for most teams this is the ‘master’ branch), create a new, local branch for the work that you are about to do.
When working in a team, having a convention for naming branches helps identify who created which branch. A simple convention that can work well and does not require any additional tools is to name your branch as follows: your initials, a slash, and then a brief summary of your work’s objective. For example, if your name was Johnny Bravo, and you were working on optimizing the speed of the signup page, you would name your branch something like
No matter how small the scope of work is, for any software that has users it is best to avoid pushing changes directly to master. Everyone can make mistakes even while making seemingly trivial changes, so by skipping a common procedure, you are reducing the chances of discovering and recovering from those mistakes quickly.
Allowing developers to push directly to master can also create accidental technical problems, such as a new team member rewriting days of development history by a forced push. For these reasons, we recommend disallowing direct push to the master branch.
Keep the Scope of the Feature Branch Small
The focus of continuous deployment is to deploy working software to production frequently and quickly. Regardless of whether a project’s deployment process is automated or not, long-living branches that accumulate days, or even weeks worth of work often create many integration problems and slow down the overall development process. Big changes are generally quite difficult to review properly, and thus they increase the chances of introducing new bugs.
To avoid these types of issues, you should limit each branch to doing one logical thing that you can ideally deploy on the same day. If you are beginning to work on a feature that cannot possibly be completed so quickly, you can hide the work in progress from your users with a feature toggle or a simple
if statement, and then continue working in small increments.
Apply BDD to Keep Your Test Suite Healthy
A comprehensive, up-to-date automated test suite is a prerequisite for the continuous deployment of any code that contains nontrivial business logic. In the process of behavior-driven development, tests are a byproduct of systematic thinking and good design.
If you are merely making changes to existing features or doing maintenance, you should strive to keep the code and the tests triggered by your code clean. Make an effort to follow the boy-scout rule: always leave the code behind in a better state than you found it.
Push the Local Branch to Run Tests in Continuous Integration
Your continuous integration (CI) service (e.g. Semaphore) should test every new branch automatically. That is, CI should “know” when a new branch is pushed to the remote Git repository, run the test suite for the branch’s latest revision, and report the results to the author(s) of the branch. Finally, all this should happen without changes or overhead in your developers’ workflow.
Semaphore automatically picks up new Git branches and runs a pre-configured build. All the developer needs to do is
Deploy to Staging if You Need to QA the Feature
Tests alone cannot tell us if, for example, a user-facing feature looks and feels absolutely right. For such reasons, it is necessary to perform manual testing or quality assurance (QA) in a staging environment.
Deploying a feature branch to a staging environment should be a straightforward and quick operation that is performed in the same way by every team member. For example, setting up a deployment pipeline on Semaphore makes deployment a unified, one-click operation for any project.
The actual deployment that runs on the CI service is based on commands and configuration previously defined by the project’s development team. Some services like Semaphore also provide “wizards” to configure deployment to popular cloud providers so that there’s no need for the developers to manually configure it even the first time they add a project.
If a project does not yet have an automated deployment procedure — ideally encapsulated with one script or command that needs to be run — then the team needs to invest in that in order to make continuous deployment possible. All major programming languages have tools designed to automate deployment (e.g. Capistrano, Gradle, etc.), while more and more applications are packaged and deployed in containers (e.g. Docker, Rocket, etc.). There is also a range of DevOps tools for infrastructure automation (e.g. Ansible, Chef, Puppet, etc.) for more complex workflows.
Open a Pull Request for Code Review
You’ve written the source code, including tests, and the functionality works as expected. It’s time for a collaborative code review.
Like everything else in the development process, code review works best in small increments and when done all the time. For this reason, it makes the most sense to establish a practice in which every pull request is reviewed by someone who did not write the proposed code. Features such as line-level comments on the source code diff are designed to facilitate a collaboration that should be both technically useful and educational.
When you open a pull request on e.g. GitHub or Bitbucket, CI services are able to send information about the “commit status” or “pull request status”, i.e. they can indicate whether the build on the latest commit in the given branch is passing or not. This provides instant feedback on whether the pull request is safe to merge or not to both the author and the rest of the team.
It’s useful to develop the habit of opening a pull request even if you are working alone. Many times you may find yourself noticing a suboptimal part of code only after you have seen the diff with new eyes on the pull request page.
Merge the Branch into Master to Deploy to Production Automatically
Depending on the outcome of the code review process, you may or may not need to return to your code and do some more tests in a staging environment. Once you are done, given that all automated tests are passing, you can be confident that the work is safe to be deployed. All that’s left to do is to merge the branch into master.
Merging a branch from a web interface, e.g. by clicking on the “Merge pull request” button on GitHub, will create a new commit on the master branch. The CI service will then pick up that commit, run tests and — this is at the core of continuous deployment — automatically deploy the latest version of master to production servers.
This process assumes that the deployment to production is configured in a similar way to deployment to staging, but with an important difference — it’s configured to run automatically, on every change in the master branch. For example, you would do this on Semaphore by selecting the “Automatic” deployment strategy and tying it with the master branch in the deployment configuration wizard.
A CI service that runs this process is also able to communicate the deployment status and keep track of a shared history for the whole team. Having the procedure unified and streamlined removes outside barriers to developer productivity.
Continuous Deployment is All About Moving Forward Faster
From a developer’s perspective, continuous deployment doesn’t add any particular overhead to the development process:
- Create and work in a feature branch
- Push branch to origin
- Wait for tests to pass in CI
- Deploy to staging, ideally with one click, if manual testing is necessary
- Open a pull request, discuss any significant changes with other team members
- Merge the feature branch to master, letting CI to deploy to production
On the process modeling side, the heart of the continuous deployment process is the CI service. It tests new branches and pull requests, performs and tracks deployment history, and provides feedback to the team.
Continuous deployment should be the goal of most companies making web-based software that are not constrained by regulatory or other requirements. If the reasons that are currently preventing introducing continuous deployment into your workflow are technical, your team should ideally reach an agreement, create a roadmap, and iteratively work towards solving all the issues in the way of making the deployment of your project(s) continuous.