Where the Problem Started

A deployment strategy is not about choosing a fashionable name. It is an operational design decision: how carefully to expose a new version, how quickly to roll back when something breaks, and what users experience during the transition. This post compares several deployment styles through that lens.

Deployment strategy matters in software development.

The factors that decide a deployment strategy include uptime, risk management, resource management, UX, and so on.

The three representative strategies are easier to compare with small code examples.

First is Blue-Green deployment.

You create two identical deployment environments, and only one environment runs at a given point in time. For example, if the current live environment, meaning the currently deployed version, is Blue, the new version starts deployment in the Green environment. Once testing in Green is complete, after the CI/CD process finishes the build and unit tests, and the application is running, traffic moves from Blue to Green.

The advantage is that if a problem occurs in the new deployment environment, you can immediately roll back to the existing environment. Also, because the traffic change happens when Green is running, uptime remains continuous and users do not experience an interruption.

The downside is that it uses a lot of resources. In a typical Blue-Green deployment, starting Green does not mean taking Blue down. If you do, the major advantage of easy rollback becomes much weaker. So from the perspective of server resources and maintenance, it can feel heavy.

Here is a simple yaml file that can be run with Github Actions for a Blue-Green workflow.

Implementation Flow

name: Blue-Green Deployment

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      # Add steps to build/test your application here
      # - name: Build and Test
      #   run: ...

      - name: Deploy to Green Environment
        run: |
          # Add your deployment scripts here
          echo "Deploying to Green Environment"
          # Example: ssh user@server 'deploy-script-green.sh'

      - name: Health Check for Green Environment
        run: |
          # Perform a health check
          echo "Checking Green Environment Health"
          # Example: curl http://green.yourdomain.com/health

      - name: Switch Traffic to Green
        if: success()
        run: |
          # Switch traffic from Blue to Green
          echo "Switching traffic to Green Environment"
          # Example: ssh user@server 'switch-traffic-to-green.sh'

      - name: Monitor Green Environment
        run: |
          # Optional: Monitor the Green environment
          echo "Monitoring Green Environment"
          # Example: Some monitoring script/logic

      - name: Rollback to Blue if Needed
        if: failure()
        run: |
          # Rollback to Blue environment in case of failure
          echo "Rolling back to Blue Environment"
          # Example: ssh user@server 'switch-traffic-to-blue.sh'

Next is Canary Deployment.

This is a “gradual” deployment that distributes traffic between the already deployed version and the new version, releasing it to some users before the application is fully deployed.

The biggest advantage is the risk management that comes from being a “gradual” deployment. By releasing to a small group of users first, you can find bugs or errors before releasing to all users. Rolling back to the previous version is also relatively simple.

The obvious downside is that because the new version is deployed to a small user pool, it is difficult to predict potential issues that may appear when a larger pool of users receives traffic. A limited release can hide issues that only appear later.

Here is a simple Github Actions yaml example.

name: Canary Deployment

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      # Add steps to build/test your application here
      # - name: Build and Test
      #   run: ...

      - name: Deploy to Canary
        run: |
          # Add your deployment script here
          echo "Deploying to Canary Environment"
          # Example: deploy-script-canary.sh

      - name: Monitor Canary Deployment
        run: |
          # Implement monitoring. This could be a script that checks the health of the application.
          echo "Monitoring Canary Deployment"
          # Example: curl http://canary.example.com/health

      - name: Gradual Rollout
        run: |
          # Gradually route more traffic to the canary
          echo "Increasing traffic to Canary"
          # Example: increase-traffic-to-canary.sh

      - name: Full Rollout
        run: |
          # If everything is fine, route all traffic to the canary (making it the new production)
          echo "Routing all traffic to Canary"
          # Example: full-traffic-to-canary.sh

The important shell script implementation and the gradual deployment itself are handled using a load balancer, service mesh such as Istio, or an orchestration tool such as k8s.

Last is Rolling Deployment.

Rolling deployment replaces the existing version piece by piece. The metaphor is close to changing the wheels while the car is still moving: traffic keeps flowing while parts of the system are gradually replaced.

The advantage is that, since deployment happens in parts, you can identify which part causes an issue, and downtime is minimized. Compared with Blue-Green, resource usage should also be noticeably lower.

The downside is that even if an issue occurs, it is not always clear whether it is a compatibility problem between the old and new versions, or whether it is simply caused by a new part of the new version. Deployment time is also a bit slower than the other strategies.

Here is a simple yaml example.

name: Rolling Deployment

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      # Build and test steps would go here
      # - name: Build
      #   run: ...

      - name: Deploy
        run: |
          # Replace with your deployment script/command
          echo "Starting Rolling Deployment"
          # For example, if using Kubernetes:
          # kubectl rollout restart deployment/myapp

          # Monitoring the rollout status
          echo "Monitoring deployment status"
          # Example: kubectl rollout status deployment/myapp

Deployment Takeaway

A good deployment strategy must match the team’s traffic scale, failure tolerance, monitoring quality, and rollback automation. Canary is not the answer for every service, and blue-green is not necessary for every small application. The real question is whether the next action is clear when a release fails.

This example is pseudocode rather than a runnable deployment script. I will cover Kubernetes-based deployment separately.