🚀

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 build
      • test_staging: Runs automated tests
  • 🔀 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 build
      • test_staging: Runs automated tests
      • deploy_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 build
      • test_production: Runs automated tests
      • deploy_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:

  1. 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
  2. Create a short-lived feature branch
    • Create a feature/... branch from main
    • Develop changes locally
    • Document relevant changes in the Unreleased section of the changelog:
    • Commit and push changes
  3. 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)
  4. 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
  5. 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

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:

  1. 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)
  2. Create a temporary release branch
    • Create a release/x.y.z branch from main
    • 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
    • Bump component version numbers
    • Commit and push changes
  3. 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
  4. Create a version tag
    • Create a tag for the version change commit
  5. Wait for the deployment pipeline to succeed
    • build_production: Creates a deployable build
    • test_production: Runs automated tests
    • deploy_production: Deploys to production environment