CI/CD
CI/CD can be read as "CI (continuous integration) and/or CD (continuous delivery)". Both practices require a rather disciplined approach to software development. Intentional Commits can help if the right goals are chosen for the intention.
What is Continuous Integration (CI)?
CI (continuous integration) is the practice of integrating changes into the mainline (main/master/trunk) continuously during development. Generally at least once per day, but often much more frequently, after minutes or hours of working, not days.
Integrating continuously into the main branch has significant advantages:
- Merge conflicts and merge mistakes – where the conflict has been resolved automatically but incorrectly or a mistake has been made while manually resolving a conflict – may occur less frequently, but more importantly, each merge conflict is much smaller and thus easier to resolve correctly.
- We can frequently build onto each others changes and find out earlier when integrating the changes with each other causes problems.
- We can automatically verify each version and find out within minutes if something is broken, if we automate the build and test process.
- We can potentially release more frequently if we additionally aim for releasability of each version (continuous delivery) and thus get early feedback about the validity and value of a change.
As a supporting practice, the build and test execution is typically automated to be able to efficiently and reproducibly run it multiple times per day.
Unfortunately, the terms CI (continuous integration), CD (continuous delivery), and the umbrella term CI/CD are often misunderstood and misused. CI is often incorrectly reduced to automation and tooling, i.e., having an automated build pipeline; some developers refer to their pipeline as “the CI (pipeline)”, despite not practicing CI, or even calling it "CI/CD (pipeline)" even though "delivery" may happen very infrequently or even manually in their context.
What is Continuous Delivery/Deployment (CD)?
Usually CD refers to continuous delivery, which is the process of creating a releasable version multiple times per day and being able to release on demand. That means that CI is practiced, multiple times per day a releasable version is created (ideally every version created with CI), and the deployment/release process is automated so that it can be released on demand at any time. (There are some additional techniques for separating deployment from release, but that distinction is out of scope here.)
Sometimes CD refers to continuous deployment, which is a special case of continuous delivery where each releasable version is also automatically deployed.
CD has the advantage that release and/or deployment can be done whenever it makes sense from a business perspective, rather than requiring a stabilization phase where the developers fix bugs in the not-yet-releasable software. The software is continuously kept stable and releasable. By being able to release more often, it also allows for getting user/customer feedback earlier and more frequently, and it reduces the risk of each individual release.
Supporting CI/CD with Intentional Commits
If we want to do CI, i.e., integrate multiple times per day, we have more rigorous expectations for each version we create. For example, when I integrate my changes, I need to make sure I don't break existing functionality. Intentional Commits does not prescribe specific rules for each commit, but it reminds us to be intentional about the commit's scope. Should I create all the functionality for my feature first and integrate it, then manually test it, then write automated tests? Or should I rather create a small piece (or vertical slice) of functionality, accompanied by automated tests, in a single commit that is already verified and ready to be integrated?
With CD, we want to be even more rigorous when it comes to the quality of each commit. Not introducing "bugs" may not be enough. If I want to add a button to the application which adds a product to the shopping cart, should I add the button first? We can't release the application with a button that does nothing (or worse, crashes the application when clicked). Can I create the functionality, accompanied by tests, where the tests are the only callers for now? Then make the UI changes afterwards in a separate commit, thus "activating" the feature? This way we can create big features in small slices that can be individually verified and deployed, even if the feature is not finished yet, since the feature is also not enabled yet. Similarly, big refactoring efforts can be done incrementally by breaking down large behavior-preserving changes into smaller ones, for example by providing two versions of an (internal) API in parallel and incrementally migrating the callers to the new API before deleting the old API at some point in the future. At any point during this effort the application remains releasable, and the effort can be chunked into small changes of way less than a day each.