Decouple CI and CD

I would like to discuss a practice that I recommend for CI/CD design: separation of CI and CD.

It always depends

One important not that this post must start with is that there is not 1 way to implement a CI/CD pipeline. There are a lot of tools and a lot of different ways to implement the the flow. That said I think it's important to know the common practices in the industry and to build our CI/CD based on the common practice. This help avoid common pitfalls

I see this recommendation as part of the modern practices of DevOps and CI/CD. This is definitely an advice that I would keep in mind when designing a DevOps workflow.

Decouple Build and Deploy

The recommendation in this post is to decouple CI (to simplify: the build part) and CD (the deployment part).

There are multiple ways to see it:

In this setup CI and CD are not 1 continuous action, they are building blocks. This doesn't mean that we can't deploy automatically the CD building block can be triggered automatically after the CI building blocks. It means that you have the control on how and when to sequence them.

A pattern that is not decoupling CI and CD can be as follows:

Representation of a coupled CI/CD

On the opposite a pattern that is decoupling CI and CD can be as follows:

Representation of a decoupled CI/CD

The benefits

There are a few benefits that I would like to highlight.

Decouple CD from your source control.

By nature the CI is more strongly coupled to source control. Services are built from the code. On the other hand CD is more coupled to the way your handle release. The idea is to have 1 more level of abstraction between your code and your production.

Source control is a very important aspects of DevOps. There are topics like monorepos, library management, branching strategies. You want to be able to experiment with those practices and modernize your source control practice without directly impacting your production environment.

Some applications may also come from different kind of sources that you don't control directly. It may be another vendor or an open source component.

This is also a discussion that will be important in the context of Microservices. One of the key aspect of Microservices are their ability to be deployed independently. Repositories for monolitic application, containers and cloud functions can be different.

Handle multiple environments

You will be often handling complex environments. You may have multiple environments, some environments may be connected to the Internet, some environments may be offline, the application source control may even be unreachable from the environment.

You may even have a number of environments that is more dynamic that you initially thought. For example because of a new request, a hotfix that need to be tested in a temporary environment.

You don't want to redesign your CI/CD for each change in the environment.

Security

There is an added benefit of using an artifact repository as intermediary: security.

An artifact can be scanned for vulnerabilities. An extra mesure that you can take is to make artifacts immuables. Once an artifact is built, if they don't change you can guarantee that a given version is correctly validated and and exact version of an artifact is deployed in an environment. It you build a service twice you can end up with 2 different services. The same source code will produce different build artifacts depending on the library used, the Docker base image used or other components that can be variable during the build phase.

GitOps

This can appear like a paradox but decoupling CI and CD is one of the key of GitOps.

GitOps practices states that each environment should be declared in a manifest, ideally in Git. So it means that the environments are directly inferred from the source control.

That said one important distinction here is that the deployment manifest is different from the application source that is used to build the artifacts.

Simplicity(?)

Decoupling introduce a bigger initial effort in the CI CD design and potentially more steps. That said, once done a less coupled architecture and based on CI and CD building blocks can also generate a system that is more simple to reason about. Less coupling tend also to simplify the workflow.

Advanced release

Release management is becoming more complex. CI and CD have been the norm for some time but there are discussion of Continuous Release. This can include simple release policies or more advanced patterns like blue/green deployment and A/B testing.

The drawback: the need of an artifact repository

The consequence of this decoupling is that a there is the need for an artifact repository. This introduce a necessary additional component in the system.

This will then depends on the technology used. If you are using containers artifact repository is pretty much a must (for image resgisty) and GitOps is becoming a standard. If you are using other technologies, there can still be a lot of benefit to this approach. The artifact repository can be a dedicated software or even a cloud storage bucket (AWS S3, Azure Storage Account, AlibabaCloud OSS,...)

Final notes....

The goal of this post is to share what are in my opinion some key aspects of modern CI CD: