In the last post, we saw the practices related to Continuous Integration (CI). We essentially discussed practices related to putting all the developers’ daily work in common into a single shared main line of code under Source Code Management tool, while ensuring quality. The next logical step would be to perform continuous deployment and put all our new features full of business value into the hands of our users, no? But before considering Continuous Deployment (which I promise, we will discuss in the next post), let’s discuss what would be the steps just before that, Continuous Delivery.
Continuous Delivery vs Continuous Deployment
A lot of people confuse those 2 types of CDs, but there is really a small difference between both. Continuous Delivery is about preparing a release that is 1 button click away from being released into Production, while Continuous Deployment makes it a step further and goes straight into Production at every code commit into the main line.
As you can imagine, the level of maturity with regards to software development practices has to be pretty high to go straight into Production at every code commit, automatically. This is why those 2 steps exists essentially; the first one exists to assess if you have the proper maturity to take the last leap or not. It is also possible that even when you will reach good maturity and attain the point where you could consider Continuous Deployments, that releases to your users will remain a business decision, triggered by non-technical people (which could also be handled using feature flags ;-).
Environments types and purposes
When we quit the world of CI, we have a packaged increment of our software. This package needs to be reused in various environments to validate various aspects beyond unit tests and static analysis. Here’s a reminder of all environment types as well as their characteristics and purposes:
ENVIRONMENT NAME(S) | PURPOSES | NOTES |
Development (dev) | Serves for local development needs | Relies on stubs to permit efficient development without all integrations with other systems |
CI – Continuous Integration | Covers builds, unit tests, packaging, static code scans | See previous post of this series for more details; again, no integration with other systems |
Integration | Serves to validate integration with other systems, end to end automated systems tests | Fully automated, end to end system testing |
User Acceptance Testing (UAT) / Quality Assurance (QA) | Serves for manual testing / QA | Ideally, as less required as possible since automation covers as much as possible. Could still be useful for exploratory testing and subjective testings. Could be merged with either Integration environment or Staging environment. |
Staging (or PreProd) | mostly to add load/performance tests | Representative of Production – does not have to match production resources, but must present the same characteristics |
Production | Serve your real customers! | The one requiring High-Availability (HA) and that needs to be ready for Disaster Recovery (DR) |
Graphically:
The more to the left, the more flexibility to develop and test the system being developed. The more to the right, the more critical the environment becomes, for high availability, access controls, etc. Again, the slight difference between Continuous Delivery and Continuous Deployment, is only in the automation between the promotion from Staging into Production.
What remains to be done before putting the new software in production?
Before trusting our software delivery pipeline to go straight into Production, here’s a list of things to be done at the Continuous Delivery level:
- Infrastructure as code
- Full automated system tests validating business value, Performance tests
- And yes, sometimes even manual tests
Infrastructure As Code
Infrastructure as Code, or IaC, refers to the practice of using software to describe the platform that will received your packaged software that was prepared at the CI level. The same mechanisms should be leveraged in all environments between Integration and Production. Small differences might exists between those environments, but there should be as little difference as possible between Staging and Production. Staging is the step where you essentially test how your software increment will behave in Production, and you want to exercise the same Infrastructure as Code to find out problems before production. It might not be the exact same scale, but it should be representative enough of your Production to find problems before hitting Prod.
- A note on IaC: Your IaC code should follow the same steps as your application code, i.e. that it should be developed using TDD, unit tested, feature tested, etc. It has to go through the very same steps as your application code, in a CI-CD pipeline.
There are a lot of tools to help you define your infrastructure as code:
- Provisioners or Configuration Management Tools such as Chef, Ansible, Puppet, etc. are tools that can help defining machines content, their desired/target configurations, etc.
- Orchestrators, such as CloudFormation, Terraform, etc. are tools that can help you define how multiple machines are interconnected together to form your global application architecture, covering aspects like Networking, Blue-Green deployments, Load Balancing, etc.
It could be interesting to have different pipelines for Infra as Code and application code and to daisy chain both together, especially if you application code has a change rate way higher that your IaC code. For similar reasons, it could be interesting to split CI and CD in different pipelines as well so you can re-deploy without re-building, etc.
System Tests, Performance Tests – Automated
Full system testing could be recommended, especially in domains having strong conformity requirements or important impacts (financial, health, etc.). Staging is your last occasion to ensure that you comply with all the rules imposed by your application domain.
Staging is also the last environment where you can validate how your application will perform when given a similar amount of resources (or at least a representative amount of resources) as in Production. Performance is one of the most important non functional aspect of your software; If your users find that your application’s latency is too high, if its responsiveness is not good, users might stop using your application altogether, even if it meets all the functional requirements. When having the same features as your competition, generally, the non functional requirements are what will make you win against them 😉
A word on Manual Testing
It is a good idea to have some Exploratory testing performed by experts, to assess qualitative aspects of your software. Often, people think that because all tests are now automated, that there is no need for manual testing; in fact, I prefer to say that repetitive tests automation frees your tests experts to perform value added tests that are way more difficult to automate and typically more subjectives. Those tests should be fast however and should not be in the way of getting the software increments in the hands of your users as soon as possible.
Next step
Once Continuous Integration and Continuous Delivery is achieved, the next step is Continuous Deployment, i.e. give it to our users. Next post will be about how to do that in a way bringing the most benefits.