Home/Learn/CI/CD & GitHub Actions/Advanced Patterns — Deploy Strategies & Release Automation

Advanced Patterns — Deploy Strategies & Release Automation

Advanced
Deployment

Production CI/CD patterns include semantic versioning automation, blue-green and canary deployments, automatic changelog generation, and deployment rollbacks triggered by health checks.

Overview

Basic pipelines build and push images. Production pipelines automate the full release lifecycle: semantic versioning (determine the next version from commit message conventions), changelog generation (from conventional commits), GitHub releases (create release with artifacts), deployment strategies (blue-green for zero-downtime switch, canary for gradual rollout), and automated rollback when health checks fail. These patterns reduce release ceremony from hours to minutes and give teams confidence to ship frequently.

Semantic Versioning & Conventional Commits

Conventional Commits is a specification for structured commit messages. Tools like semantic-release or release-please parse these messages to automatically determine the next semantic version and generate changelogs.

Conventional commits and release-please automation
# Conventional Commit format:

# <type>(<scope>): <description>

# [optional body]

# [optional footer]



# Types:

# feat:     new feature → bumps MINOR version (1.0.0 → 1.1.0)

# fix:      bug fix     → bumps PATCH version (1.0.0 → 1.0.1)

# docs:     documentation only

# refactor: code change, no feature/fix

# test:     adding tests

# chore:    maintenance

# BREAKING CHANGE: footer → bumps MAJOR version (1.0.0 → 2.0.0)



# Examples:

# feat(auth): add OAuth2 login support

# fix(api): correct null pointer in user endpoint

# feat!: redesign API response format    ← ! = breaking change

# feat(payments): add Stripe integration

#

# BREAKING CHANGE: The /users endpoint now returns camelCase fields



# .github/workflows/release.yml — automatic releases with release-please

name: Release Please



on:

  push:

    branches: [main]



jobs:

  release:

    runs-on: ubuntu-latest

    permissions:

      contents: write

      pull-requests: write

    steps:

      - uses: google-github-actions/release-please-action@v4

        id: release

        with:

          release-type: node         # parses package.json for version

          token: ${{ secrets.GITHUB_TOKEN }}

      # release-please opens a "Release PR" that:

      # 1. Bumps version in package.json

      # 2. Generates CHANGELOG.md from commits

      # 3. When merged, creates a GitHub Release + git tag

Blue-Green & Canary Deployments

Blue-green switches 100% of traffic instantly between two identical environments. Canary gradually shifts traffic, monitoring error rates before full rollout.

Blue-green deployment with GitHub Actions and Kubernetes
# Blue-Green Deployment (Kubernetes + GitHub Actions)

# Two identical Deployments (blue=current, green=new)

# Switch traffic by updating Service selector



# .github/workflows/blue-green.yml

jobs:

  deploy:

    runs-on: ubuntu-latest

    steps:

      - uses: actions/checkout@v4



      # Determine current active color

      - name: Get active slot

        id: slot

        run: |

          CURRENT=$(kubectl get service my-app -o jsonpath='{.spec.selector.slot}')

          if [ "$CURRENT" = "blue" ]; then

            echo "deploy_slot=green" >> $GITHUB_OUTPUT

            echo "active_slot=blue" >> $GITHUB_OUTPUT

          else

            echo "deploy_slot=blue" >> $GITHUB_OUTPUT

            echo "active_slot=green" >> $GITHUB_OUTPUT

          fi



      # Deploy to inactive slot (no traffic yet)

      - name: Deploy to ${{ steps.slot.outputs.deploy_slot }}

        run: |

          kubectl set image deployment/my-app-${{ steps.slot.outputs.deploy_slot }} \

            app=ghcr.io/org/my-app:${{ github.sha }}

          kubectl rollout status deployment/my-app-${{ steps.slot.outputs.deploy_slot }}



      # Smoke test the inactive slot (internal URL)

      - name: Smoke test

        run: curl -f http://my-app-${{ steps.slot.outputs.deploy_slot }}-internal/health



      # Switch traffic: update Service selector

      - name: Switch traffic to ${{ steps.slot.outputs.deploy_slot }}

        run: |

          kubectl patch service my-app -p \

            '{"spec":{"selector":{"slot":"${{ steps.slot.outputs.deploy_slot }}"}}}'



      # Keep old slot running (instant rollback by re-patching selector)

      - name: Annotate for rollback

        run: |

          kubectl annotate deployment my-app \

            last-active-slot=${{ steps.slot.outputs.active_slot }} --overwrite

Automated Rollback on Health Failure

The safest deployments include an automatic rollback step that triggers if health checks fail after deployment — no manual intervention needed.

Deploy with automatic rollback on health failure
# .github/workflows/deploy-with-rollback.yml

jobs:

  deploy:

    runs-on: ubuntu-latest

    environment: production

    steps:

      - uses: actions/checkout@v4



      # Record current image for rollback

      - name: Save current image

        id: current

        run: |

          CURRENT_IMAGE=$(kubectl get deployment my-app \

            -o jsonpath='{.spec.template.spec.containers[0].image}')

          echo "image=$CURRENT_IMAGE" >> $GITHUB_OUTPUT



      # Deploy new image

      - name: Deploy

        id: deploy

        run: |

          kubectl set image deployment/my-app app=ghcr.io/org/my-app:${{ github.sha }}

          kubectl rollout status deployment/my-app --timeout=5m



      # Health check — wait for service to stabilise

      - name: Health check

        id: health

        run: |

          for i in {1..12}; do

            STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://api.myapp.com/health)

            if [ "$STATUS" = "200" ]; then

              echo "Health check passed"

              exit 0

            fi

            echo "Health check attempt $i/12 failed (status $STATUS), retrying in 10s..."

            sleep 10

          done

          echo "Health checks failed after 2 minutes"

          exit 1



      # Automatic rollback on health failure

      - name: Rollback on failure

        if: failure() && steps.deploy.outcome == 'success'

        run: |

          echo "Deployment failed health checks — rolling back to ${{ steps.current.outputs.image }}"

          kubectl set image deployment/my-app app=${{ steps.current.outputs.image }}

          kubectl rollout status deployment/my-app --timeout=5m

          echo "Rollback complete" >> $GITHUB_STEP_SUMMARY



      # Notify on success or failure

      - name: Notify Slack

        if: always()

        uses: slackapi/slack-github-action@v1

        with:

          payload: |

            {

              "text": "Deploy ${{ job.status }}: my-app:${{ github.sha }}"

            }

        env:

          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Key Points to Remember

  • 1Conventional Commits enable automatic semantic versioning: feat → minor bump, fix → patch, BREAKING CHANGE → major.
  • 2release-please opens "Release PRs" that auto-bump versions and generate changelogs.
  • 3Blue-green: instant traffic switch between two live environments; rollback = switch back (seconds).
  • 4Canary: gradual traffic shift with metric monitoring; safer but more complex to implement.
  • 5Automatic rollback on health failure makes deployments safe without requiring 24/7 on-call monitoring.
  • 6Always capture the current image/revision before deploying so rollback has a known good target.

Interview Questions

Sign in to ask Aria
1

What is the difference between blue-green and canary deployment?

2

How do you implement automatic rollback in a CI/CD pipeline?

Ask Aria about Advanced Patterns — Deploy Strategies & Release Automation

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…