Continuous Delivery
Streamline the development, integration and deployment of frequent, small changes
Continuous Delivery Principles
The DevOps Research and Assessment program (DORA) conducted statistical research by evaluating the development practices of over 23000 teams from all kinds of companies. They published their results in 2018 in the book "Accelerate: Building and Scaling High Performing Technology Organizations" which gained huge popularity and acknowledgement (e.g. by Google Cloud). The research found four relevant metrics for development team performance:
- 📈 Lead Time for Changes: The amount of time it takes a commit to get into production.
- 📈 Deployment Frequency: How often an organization successfully releases to production.
- 📈 Change Failure Rate: The percentage of deployments causing a failure in production.
- 📈 Time to Restore Service: How long it takes an organization to recover from a failure in production.
The research shows that the adoption of Continuous Delivery has significant impact on development team performance. Continuous Delivery means developing and integrating changes in small chunks while the software is always kept in a releasable state. This increases the lead time and deployment frequency.
Branching and Deployment
Using Trunk-Based Development as a branching model improves Continuous Delivery. A single central branch, known as the trunk, serves as the integration point for all changes such as new features or hotfixes. All environments are deployed from the trunk.
- 🔀 Feature Branches (feature/...)
- Feature branches are short-lived (~1 day)
- Unfinished features are integrated in a deactivated state (hardcoded or via feature toggle)
- Activating a feature can be implemented in a separate change
- 🔀 Pull Requests (feature/... -> main)
- Triggers the pull request integration pipeline with the following jobs:
build_staging
: Creates a deployable buildtest_staging
: Runs automated tests
- Triggers the pull request integration pipeline with the following jobs:
- 🔀 Trunk (main)
- Contains only stable/deployable integration states
- Protected against direct pushes
- Triggers the
staging
deployment pipeline with the following jobs:build_staging
: Creates a deployable buildtest_staging
: Runs automated testsdeploy_staging
: Deploys to staging environment
- 🏷️ Release Tags (x.y.z)
- Marks releasable integration states with a SemVer version number
- Triggers the
production
deployment pipeline with the following jobs:build_production
: Creates a deployable buildtest_production
: Runs automated testsdeploy_production
: Deploys to production environment
Development Setup
Developers need to set up a local development environment on their computers and become familiar how to develop, debug and test code changes locally. For onboarding purposes, it is a good idea to document all the relevant steps for that. This will most likely include the following things:
- Install tools
- Install dependencies
- Set up local environment variables
- Start debugging
- Create a local build
- Run tests locally
Development Workflow
The development workflow describes all steps to transform planned issues into code changes that are integrated into the main branch but not yet released. Here is a sample description of such a workflow:
- Assign issue
- Find an issue that is assigned to your team and the current milestone
- Assign the issue to yourself
- Change the issue state to
In Progress
- Create a short-lived feature branch
- Create a
feature/...
branch frommain
- Develop changes locally
- Document relevant changes in the
Unreleased
section of the changelog: - Commit and push changes
- Create a
- Create a pull request
- Create a pull request
feature/... -> main
- Reference the issue in the pull request description to automatically close it later (e.g.
closes #42
)
- Create a pull request
- Get peer review approval
- Wait for the pull request pipeline to succeed
- Ask a teammate to peer review changes and wait for feedback
- Discuss any feedback comments on the pull request and make changes
- Repeat until pull request is approved with a 👍 reaction
- Merge pull request
- Squash commits so that
main
only contains stable/deployable integration states - Wait for the deployment pipeline to succeed
- Close the pull request and delete the feature branch
- Close the corresponding issue
- Wait for the
staging
deployment pipeline to succeed
- Squash commits so that
Release Workflow
The release workflow bundles integrated code changes into releases and makes them available to users. Each release is assigned a semantic version number. Here is a sample description of such a workflow:
- Determine a version number
- Open the changelog and determine the next semantic version number based on the unreleased changes:
- Breaking changes increase the major version (
1.2.3 -> 2.0.0
) - Changes increase the minor version (
1.2.3 -> 1.3.0
) - Fixes increase the patch version (
1.2.3 -> 1.2.4
)
- Breaking changes increase the major version (
- Open the changelog and determine the next semantic version number based on the unreleased changes:
- Create a temporary release branch
- Create a
release/x.y.z
branch frommain
- Finalize release notes in changelog
- Create a new section with the version and the current date (
# x.y.z / YYYY-MM-DD
) - Move over everything previously added to the
Unreleased
section
- Create a new section with the version and the current date (
- Bump component version numbers
- Commit and push changes
- Create a
- Create and merge a pull request
- Create a pull request
release/x.y.z -> main
- Merge and close the pull request, delete the feature branch
- Create a pull request
- Create a version tag
- Create a tag for the version change commit
- Wait for the deployment pipeline to succeed
build_production
: Creates a deployable buildtest_production
: Runs automated testsdeploy_production
: Deploys to production environment