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
- Risk Mitigation - Limit blast radius of problematic releases
- Zero Downtime - Keep applications available during deployments
- Fast Rollback - Quickly revert to previous versions if issues arise
- Gradual Rollout - Test changes with subset of users before full deployment
- A/B Testing - Compare versions to measure impact
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:
- Simple to implement
- Clean slate for new version
- No version compatibility concerns
Cons:
- Application downtime during deployment
- No gradual rollout
- Risky for critical applications
Use when:
- Application cannot run multiple versions simultaneously
- Downtime is acceptable
- Development or test environments
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:
- No downtime
- Gradual rollout reduces risk
- Easy rollback with
kubectl rollout undo
Cons:
- Both versions run simultaneously
- Slower than recreate
- Requires backward compatibility
Use when:
- Zero downtime is required
- Application supports multiple versions running concurrently
- Gradual rollout is preferred
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:
- Instant cutover
- Fast rollback by switching back
- Full testing in production-like environment
Cons:
- Requires double infrastructure (expensive)
- Database migrations are complex
- Stateful applications need careful handling
Use when:
- Instant rollback is critical
- Budget allows double infrastructure
- Need production-like testing environment
- Stateless applications
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:
- Minimal risk with gradual rollout
- Real production testing with small user subset
- Data-driven deployment decisions
- Easy rollback at any stage
Cons:
- Requires sophisticated traffic management
- Complex monitoring setup
- Slower deployment process
- Both versions run simultaneously
Use when:
- Risk reduction is paramount
- Real-world validation is needed before full rollout
- Infrastructure supports traffic splitting
- Monitoring is comprehensive
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:
- Data-driven feature decisions
- Compare user behavior and metrics
- Can test multiple variants
- Business metric optimization
Cons:
- Requires feature flag infrastructure
- Complex user segmentation
- Longer deployment duration
- Analytics integration needed
Use when:
- Testing business impact of features
- Comparing multiple feature variations
- Data-driven product decisions are important
- User experience optimization is goal
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:
- Zero risk to users
- Real production testing
- Performance comparison under load
- Identify issues before user impact
Cons:
- Requires double compute resources
- Cannot test write operations safely
- Complex logging and monitoring
- Higher infrastructure cost
Use when:
- Testing high-risk changes
- Performance benchmarking needed
- Load testing in production
- Validating refactors or rewrites
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
- Recreate strategy is simple but causes downtime
- Rolling updates provide zero-downtime deployments with gradual instance replacement
- Blue-green deployments enable instant cutover and fast rollback but require double infrastructure
- Canary deployments minimize risk by gradually shifting traffic based on metrics
- A/B testing compares feature variants for data-driven decisions
- Shadow deployments test new versions with production traffic without user impact
- Feature flags decouple deployment from feature release
- Database migrations require backward-compatible changes across multiple phases
- Choose strategy based on risk tolerance, budget, and application characteristics
- Monitoring and automated rollback are critical for advanced strategies
- Consider stateful vs stateless applications when selecting strategies
Next Steps: Evaluate current deployment approach, implement traffic splitting for canary deployments, establish rollback procedures, and integrate monitoring with deployment automation.