CI/CD Patterns
Pipeline Stages (Universal)
1. Lint / Format Check
2. Unit Tests
3. Build Artifact / Docker Image
4. Integration Tests
5. Security Scan (SAST/SCA)
6. Push to Registry
7. Deploy to Staging
8. Smoke Tests
9. Deploy to Production (manual gate or auto)
10. Post-deploy Health Check
Blue-Green Deployment
# Two identical environments; switch traffic at load balancer
# Blue = current live, Green = new version
# 1. Deploy new version to Green
kubectl apply -f deployment-green.yaml
# 2. Wait for Green to be ready
kubectl rollout status deployment/myapp-green
# 3. Run smoke tests against Green
./scripts/smoke-test.sh https://green.internal
# 4. Switch traffic (update Service selector)
kubectl patch service myapp -p '{"spec":{"selector":{"version":"green"}}}'
# 5. Monitor — rollback in seconds if needed
kubectl patch service myapp -p '{"spec":{"selector":{"version":"blue"}}}'
Canary Deployment (nginx ingress)
# 90% → stable, 10% → canary
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-canary
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-canary
port:
number: 80
Rolling Deployment (Kubernetes)
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # Extra pods during update
maxUnavailable: 0 # Never reduce capacity
Artifact Versioning
# Semantic: image:1.2.3
# SHA-based (preferred for traceability):
IMAGE_TAG="${CI_COMMIT_SHA:0:8}"
docker build -t "registry/myapp:${IMAGE_TAG}" .
docker push "registry/myapp:${IMAGE_TAG}"
# Also tag with branch for humans:
docker tag "registry/myapp:${IMAGE_TAG}" "registry/myapp:main-latest"
Pipeline Caching
# GitHub Actions cache
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
# Docker layer cache via buildx
- uses: docker/build-push-action@v5
with:
cache-from: type=registry,ref=registry/myapp:buildcache
cache-to: type=registry,ref=registry/myapp:buildcache,mode=max
Environment Promotion Gates
Dev → Staging : auto on merge to main
Staging → Prod : manual approval required
Prod rollback : automated on error-rate > 1% SLO breach
Rollback Strategy
# Kubernetes: rollback to previous revision
kubectl rollout undo deployment/myapp
# Rollback to specific revision
kubectl rollout history deployment/myapp
kubectl rollout undo deployment/myapp --to-revision=3
# Helm rollback
helm rollback myapp 2
Feature Flags for Safe Deploys
# Deploy code dark, enable via flag — decouple deploy from release
if feature_flag("new-checkout-flow", user_id=user.id):
return new_checkout()
return old_checkout()
Key Rules
- Deploy artifacts, not source code — build once, promote the same image
- Every deployment must be reversible in < 5 minutes
- Never deploy Friday afternoons or before holidays
- Use environment parity: staging should mirror prod data shape
- Tag every release with git SHA for traceability