Deployment Strategies

What are Deployment Strategies?

Deployment strategies define how new application versions are released to production environments. Different strategies balance risk, downtime, complexity, and rollback capabilities to meet specific business and technical requirements.

Choosing the right deployment strategy minimizes risk while enabling rapid, reliable software delivery.


Why Deployment Strategies Matter


Deployment Strategy Overview

graph TD A["Deployment Strategies"] -->B["Recreate
All at once"] A -->C["Rolling
Gradual replacement"] A -->D["Blue-Green
Environment swap"] A -->E["Canary
Traffic shift"] A -->F["A/B Testing
Feature comparison"] A -->G["Shadow
Parallel testing"] B -->H["Fast, downtime"] C -->I["No downtime, slower"] D -->J["Instant rollback"] E -->K["Risk reduction"] F -->L["User feedback"] G -->M["Production testing"] style B fill:#ffcccc style C fill:#fff3e0 style D fill:#e1f5ff style E fill:#e8f5e9 style F fill:#e8f5e9 style G fill:#e1f5ff

Recreate Strategy

Stop all existing instances, then deploy new version. Simple but causes downtime.

Process

sequenceDiagram participant Old as Old Version (v1) participant LB as Load Balancer participant New as New Version (v2) participant Users as Users Users->>LB: Traffic to v1 LB->>Old: Route traffic Note over Old: Stop v1 instances Old->>Old: Shutdown Note over New: Deploy v2 New->>New: Start instances Users->>LB: Traffic during deployment LB->>Users: Downtime/503 errors Note over New: v2 ready LB->>New: Route to v2

Implementation

# Kubernetes Deployment apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 3 strategy: type: Recreate template: spec: containers: - name: app image: myapp:v2

Characteristics

Pros:

Cons:

Use when:


Rolling Update Strategy

Gradually replace instances with new version, maintaining availability.

Process

sequenceDiagram participant V1 as Version 1 participant V2 as Version 2 participant LB as Load Balancer Note over V1: 4 instances running v1 V1->>V1: Instance 1 stops V2->>V2: Instance 1 starts v2 Note over V1,V2: 3 v1, 1 v2 V1->>V1: Instance 2 stops V2->>V2: Instance 2 starts v2 Note over V1,V2: 2 v1, 2 v2 V1->>V1: Instance 3 stops V2->>V2: Instance 3 starts v2 Note over V1,V2: 1 v1, 3 v2 V1->>V1: Instance 4 stops V2->>V2: Instance 4 starts v2 Note over V2: 4 instances running v2

Implementation

# Kubernetes Deployment apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: replicas: 4 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # Max instances above desired count maxUnavailable: 1 # Max instances below desired count template: spec: containers: - name: app image: myapp:v2 readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 5 # Azure Pipelines strategy: rolling: maxParallel: 2 preDeploy: steps: - script: echo "Pre-deployment validation" deploy: steps: - script: kubectl apply -f deployment.yaml postRouteTraffic: steps: - script: kubectl rollout status deployment/myapp

Characteristics

Pros:

Cons:

Use when:


Blue-Green Deployment

Maintain two identical production environments, switch traffic instantly.

Process

graph TD A["Blue Environment
Version 1
Active"] -->B["Load Balancer
Routes to Blue"] C["Green Environment
Version 2
Idle"] -->D["Deploy v2 to Green"] D -->E["Test Green
Environment"] E -->F{Tests Pass?} F -->|Yes| G["Switch LB to Green"] F -->|No| H["Keep Blue Active"] G -->I["Green Active
Blue becomes standby"] style A fill:#add8e6 style C fill:#90ee90 style I fill:#90ee90 style H fill:#add8e6

Implementation

# Kubernetes with Services # Blue deployment (current production) apiVersion: apps/v1 kind: Deployment metadata: name: myapp-blue labels: version: blue spec: replicas: 3 selector: matchLabels: app: myapp version: blue template: metadata: labels: app: myapp version: blue spec: containers: - name: app image: myapp:v1 --- # Green deployment (new version) apiVersion: apps/v1 kind: Deployment metadata: name: myapp-green labels: version: green spec: replicas: 3 selector: matchLabels: app: myapp version: green template: metadata: labels: app: myapp version: green spec: containers: - name: app image: myapp:v2 --- # Service initially pointing to blue apiVersion: v1 kind: Service metadata: name: myapp spec: selector: app: myapp version: blue # Switch to green after validation ports: - port: 80 targetPort: 8080 # Switch traffic to green # kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'

Characteristics

Pros:

Cons:

Use when:


Canary Deployment

Gradually shift traffic from old to new version, monitoring metrics.

Process

graph LR A["100% traffic
Version 1"] -->B["90% v1
10% v2"] B -->C["75% v1
25% v2"] C -->D["50% v1
50% v2"] D -->E["25% v1
75% v2"] E -->F["0% v1
100% v2"] C -->|Issues detected| G["Rollback to v1"] style A fill:#ffcccc style F fill:#ccffcc style G fill:#ffcccc

Implementation

# Using Istio for traffic splitting apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: myapp spec: hosts: - myapp.example.com http: - match: - uri: prefix: "/" route: - destination: host: myapp-v1 subset: v1 weight: 90 - destination: host: myapp-v2 subset: v2 weight: 10 # Start with 10% canary traffic --- # Gradually increase canary traffic # weight: 25, then 50, then 75, then 100 # Azure Pipelines with canary strategy: canary: increments: [10, 25, 50, 100] preDeploy: steps: - script: kubectl apply -f deployment-v2.yaml routeTraffic: steps: - script: | # Update traffic split to $(strategy.increment)% kubectl patch virtualservice myapp --type=json \ -p='[{"op":"replace","path":"/spec/http/0/route/1/weight","value":$(strategy.increment)}]' postRouteTraffic: steps: - script: | echo "Monitoring metrics for $(strategy.increment)% traffic" sleep 300 # Wait 5 minutes - script: | # Check error rate ERROR_RATE=$(curl -s metrics-api.example.com/error-rate) if (( $(echo "$ERROR_RATE > 1.0" | bc -l) )); then echo "Error rate too high: $ERROR_RATE%" exit 1 fi on: failure: steps: - script: | echo "Rolling back canary" kubectl patch virtualservice myapp --type=json \ -p='[{"op":"replace","path":"/spec/http/0/route/1/weight","value":0}]'

Characteristics

Pros:

Cons:

Use when:


A/B Testing Deployment

Route users to different versions for feature comparison and testing.

Process

graph TD A["Incoming Users"] -->B{User Segment} B -->|Group A
50%| C["Version A
Original Feature"] B -->|Group B
50%| D["Version B
New Feature"] C -->E["Collect Metrics"] D -->E E -->F{Which performs
better?} F -->|Version A| G["Roll back B"] F -->|Version B| H["Promote B to 100%"] style C fill:#e1f5ff style D fill:#fff3e0

Implementation

# Feature flag-based routing apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: myapp-ab spec: hosts: - myapp.example.com http: - match: - headers: ab-test: exact: "variant-b" route: - destination: host: myapp-v2 - route: - destination: host: myapp-v1 # Application code with feature flags # JavaScript example if (featureFlags.isEnabled('new-checkout', userId)) { // Route to version B return renderNewCheckout(); } else { // Route to version A return renderOldCheckout(); }

Characteristics

Pros:

Cons:

Use when:


Shadow Deployment

Run new version alongside production, duplicate traffic for testing without affecting users.

Process

sequenceDiagram participant User as User participant Prod as Production v1 participant Shadow as Shadow v2 participant Monitor as Monitoring User->>Prod: Request Prod->>Prod: Process request Prod->>User: Response (real) Prod->>Shadow: Duplicate request Shadow->>Shadow: Process request Shadow->>Monitor: Log response (discarded) Shadow->>Monitor: Log metrics/errors Note over Shadow: Response not sent to user

Implementation

# Istio traffic mirroring apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: myapp-shadow spec: hosts: - myapp.example.com http: - route: - destination: host: myapp-v1 weight: 100 mirror: host: myapp-v2 # Shadow version mirrorPercentage: value: 100 # Mirror 100% of traffic

Characteristics

Pros:

Cons:

Use when:


Feature Flags

Control feature deployment independently from code deployment.

# Feature flag configuration features: new-checkout: enabled: true rollout: 25 # 25% of users whitelist: - user123 - user456 beta-dashboard: enabled: false # Application code if (featureFlags.isEnabled('new-checkout', user.id)) { return <NewCheckoutComponent />; } else { return <OldCheckoutComponent />; } # Progressive rollout # Day 1: 10% rollout # Day 2: 25% rollout # Day 3: 50% rollout # Day 4: 100% rollout

Comparison Matrix

Strategy Downtime Rollback Speed Resource Cost Complexity Risk
Recreate High Slow Low Low High
Rolling None Medium Low Medium Medium
Blue-Green None Instant High (2x) Medium Low
Canary None Fast Medium High Very Low
A/B Testing None Medium Medium High Low
Shadow None N/A High (2x) High None

Database Migration Considerations

Backward-Compatible Migrations

-- Version 1: Add new column (nullable) ALTER TABLE users ADD COLUMN email VARCHAR(255); -- Deploy application v2 (uses email column) -- Version 2: Populate data UPDATE users SET email = legacy_email WHERE email IS NULL; -- Version 3: Make column required (after all instances updated) ALTER TABLE users ALTER COLUMN email SET NOT NULL; -- Version 4: Remove old column ALTER TABLE users DROP COLUMN legacy_email;

Multi-Phase Deployment

graph LR A["Phase 1:
Add new column"] -->B["Phase 2:
Deploy app v2"] B -->C["Phase 3:
Migrate data"] C -->D["Phase 4:
Make required"] D -->E["Phase 5:
Remove old column"] style A fill:#e1f5ff style B fill:#fff3e0 style C fill:#fff3e0 style D fill:#e8f5e9 style E fill:#ccffcc

Real-World Deployment Pipeline

trigger: - main stages: - stage: Build jobs: - job: BuildJob steps: - script: docker build -t myapp:$(Build.BuildId) . - script: docker push myapp:$(Build.BuildId) - stage: DeployCanary jobs: - deployment: CanaryDeploy environment: production strategy: canary: increments: [10, 25, 50, 100] preDeploy: steps: - script: | kubectl set image deployment/myapp-canary \ app=myapp:$(Build.BuildId) kubectl rollout status deployment/myapp-canary routeTraffic: steps: - script: | # Update traffic split kubectl patch virtualservice myapp -p '{ "spec": { "http": [{ "route": [ {"destination": {"host": "myapp-stable"}, "weight": $((100-$(strategy.increment)))}, {"destination": {"host": "myapp-canary"}, "weight": $(strategy.increment)} ] }] } }' postRouteTraffic: steps: - script: | echo "Monitoring for 5 minutes at $(strategy.increment)% traffic" sleep 300 - script: | # Check metrics ERROR_RATE=$(curl -s http://metrics/error-rate) LATENCY_P95=$(curl -s http://metrics/latency-p95) if (( $(echo "$ERROR_RATE > 1.0" | bc -l) )); then echo "Error rate too high: $ERROR_RATE%" exit 1 fi if (( $(echo "$LATENCY_P95 > 500" | bc -l) )); then echo "Latency too high: ${LATENCY_P95}ms" exit 1 fi echo "Metrics look good, proceeding" on: failure: steps: - script: | echo "Rolling back canary deployment" kubectl patch virtualservice myapp -p '{ "spec": { "http": [{ "route": [ {"destination": {"host": "myapp-stable"}, "weight": 100}, {"destination": {"host": "myapp-canary"}, "weight": 0} ] }] } }' - script: | # Send alert curl -X POST $SLACK_WEBHOOK \ -d '{"text":"Canary deployment failed and rolled back"}' success: steps: - script: | echo "Canary deployment successful" # Promote canary to stable kubectl set image deployment/myapp-stable \ app=myapp:$(Build.BuildId)

Key Takeaways

Next Steps: Evaluate current deployment approach, implement traffic splitting for canary deployments, establish rollback procedures, and integrate monitoring with deployment automation.