Introduction
CI/CD transforms how teams deliver software. Automated pipelines catch bugs early, speed up releases, and make deployments routine. This guide covers best practices for building effective CI/CD workflows.
CI/CD Fundamentals
What Is CI?
Continuous Integration: Automatically build and test code changes.
- Frequent merges
- Automated builds
- Automated tests
- Fast feedback
What Is CD?
Continuous Delivery/Deployment:
- Automated release process
- Deploy to production ready state
- Manual approval for deployment
- Or fully automated (CD)
CI/CD Pipeline
Code โ Build โ Test โ Stage โ Deploy
Version Control Strategy
Branching Models
Git Flow
- main: Production releases
- develop: Integration branch
- feature/: New features
- hotfix/: Emergency fixes
Trunk-Based Development
- Short-lived branches (<2 days)
- Direct commits to main
- Feature flags for incomplete features
- Preferred for modern teams
Commit Best Practices
# Good commit messages
git commit -m "Add user authentication flow"
git commit -m "Fix: Resolve null pointer in user service
- Add null check before processing
- Add unit test for edge case
- Update documentation"
# Conventional commits
git commit -m "feat: add password reset functionality"
git commit -m "fix: resolve memory leak in cache"
git commit -m "docs: update API documentation"
Building CI Pipeline
Basic CI Setup
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
- name: Build
run: npm run build
Pipeline Stages
- Checkout: Get code
- Install: Dependencies
- Lint: Code style
- Test: Unit tests
- Build: Compile/bundle
- Security: Scan for vulnerabilities
- Artifact: Store build output
Testing in CI
Test Pyramid
/\
/E2E\ Few, expensive
/------\
/Integration\ Some
/------ ----\
/ Unit Tests \ Many, fast
/----------------\
Unit Tests
- name: Run unit tests
run: npm test -- --coverage
Integration Tests
- name: Run integration tests
run: npm run test:integration
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
E2E Tests
- name: E2E Tests
run: npm run test:e2e
env:
BASE_URL: ${{ secrets.STAGING_URL }}
CD Pipeline
Deployment Strategies
Blue-Green Deployments
- name: Deploy to blue
run: kubectl apply -f blue-deployment.yaml
- name: Run smoke tests
run: ./smoke-tests.sh blue
- name: Switch traffic
run: kubectl patch service -p '{"selector":{"version":"blue"}}'
Canary Deployments
- name: Deploy canary (10%)
run: kubectl apply -f canary-deployment.yaml
- name: Monitor metrics
run: ./monitor.sh
- name: Promote if healthy
run: kubectl scale deployment app --replicas=10
Rolling Deployments
- name: Rolling update
run: kubectl rollout status deployment/app
Environment Strategy
Development โ Staging โ Production
(auto) (auto) (manual)
Security
Secrets Management
- name: Login to registry
uses: docker/login-action@v3
with:
username: ${{ secrets.REGISTRY_USER }}
password: ${{ secrets.REGISTRY_TOKEN }}
Dependency Scanning
- name: Scan dependencies
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Container Scanning
- name: Scan Docker image
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:${{ github.sha }}'
Monitoring Deployments
Health Checks
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Rollback Strategies
- name: Rollback on failure
if: failure()
run: |
kubectl rollout undo deployment/app
echo "Deployment rolled back"
Best Practices
Speed
- Cache dependencies
- Run tests in parallel
- Use matrix builds
- Skip unnecessary steps
Reliability
- Idempotent pipelines
- Retry transient failures
- Timeout long steps
- Notify on failures
Maintainability
- Modular stages
- Shared configurations
- Documentation
- Version control
Tools
CI/CD Platforms
- GitHub Actions: GitHub integration
- GitLab CI: Built-in CI/CD
- Jenkins: Open source
- CircleCI: Cloud CI/CD
- Argo CD: GitOps
- Tekton: Kubernetes-native CI/CD
- Flux: GitOps for Kubernetes
Infrastructure as Code
- Terraform
- Ansible
- Pulumi
GitOps Fundamentals
GitOps is a modern approach to CI/CD that uses Git as the single source of truth:
# GitOps workflow
workflow:
1. Developer pushes code to Git
2. CI pipeline builds and tests
3. CD tool detects changes in Git
4. Changes are automatically deployed
5. Kubernetes cluster state matches Git
ArgoCD Example
# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/myorg/my-app.git
targetRevision: HEAD
path: deploy
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
Flux CD Example
# GitRepository
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: my-app
namespace: flux-system
spec:
url: https://github.com/myorg/my-app
ref:
branch: main
---
# Kustomization
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
namespace: flux-system
spec:
sourceRef:
kind: GitRepository
name: my-app
path: ./deploy
prune: true
selfHeal: true
Progressive Delivery
Progressive Delivery Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Progressive Delivery Strategies โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Canary โโโบ Increase traffic โโโบ Full rollout โ
โ โ โ
โ โโโโบ Monitor metrics โโโบ Rollback if unhealthy โ
โ โ
โ Blue/Green โโโบ Test new โโโบ Switch traffic โ
โ โ
โ Rolling โโโบ Gradual replacement โโโบ Complete โ
โ โ
โ Feature Flags โโโบ Toggle features โโโบ Cleanup โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Feature Flags
// Feature flag with LaunchDarkly
const LaunchDarkly = require('launchdarkly-node-server-sdk');
const ldClient = LaunchDarkly.init(process.env.LD_KEY);
async function serveUser(userId) {
const showNewFeature = await ldClient.boolVariation(
'new-payment-flow',
{ key: userId },
false
);
if (showNewFeature) {
return renderNewPaymentFlow();
}
return renderLegacyPaymentFlow();
}
# GitHub Actions with feature flags
- name: Deploy canary
run: |
ldflags="-X main.featureEnabled=${{ steps.variation.value }}"
go build -ldflags "$ldflags" -o myapp
Flagger (Progressive Delivery for Kubernetes)
# Canary deployment with Flagger
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: my-app
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: my-app
service:
port: 80
analysis:
interval: 1m
threshold: 10
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
threshold: 99
interval: 1m
- name: request-duration
threshold: 500
interval: 1m
Testing Strategies
Contract Testing
# GitHub Actions with contract testing
- name: Run contract tests
run: |
npm run test:contracts
env:
PACT_BROKER_URL: ${{ secrets.PACT_BROKER_URL }}
PACT_BROKER_TOKEN: ${{ secrets.PACT_BROKER_TOKEN }}
- name: Publish contract
run: |
npm run publish:contracts
// Consumer test (Pact)
describe('Order API', () => {
it('should accept valid orders', () => {
return pact
.addInteraction({
given: 'the service accepts valid orders',
willRespondWith: {
status: 201,
body: like({ orderId: '123' }),
},
})
.executeTest((mockServer) => {
return fetch(`${mockServer.url}/orders`, {
method: 'POST',
body: JSON.stringify({ product: 'Widget' }),
});
});
});
});
Chaos Testing in CI
# GitHub Actions with chaos testing
- name: Install LitmusChaos
run: |
kubectl apply -f https://litmuschaos.io/litmus-operator-v3.0.0.yaml
- name: Run pod kill chaos
run: |
kubectl apply -f - <<EOF
apiVersion: litmuschaos.io/v1alpha1
kind: ChaosEngine
metadata:
name: pod-kill-chaos
namespace: default
spec:
appinfo:
appns: default
applabel: "app=myapp"
chaosServiceAccount: litmus-admin
experiments:
- name: pod-delete
spec:
components:
env:
- name: TOTAL_CHAOS_DURATION
value: '30'
EOF
Supply Chain Security
SLSA Compliance
# .github/workflows/slsa.yml
name: SLSA Build
on: [push]
jobs:
build:
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Build
id: build
run: |
echo "::set-output name=digest::$(sha256sum myapp | cut -d' ' -f1)"
go build -o myapp
- name: Attest
uses: slsa-framework/slsa-github-generator/actions/attest@v1
with:
subject: "myapp"
digest: ${{ steps.build.outputs.digest }}
predicate: predicate.json
SBOM Generation
- name: Generate SBOM
uses: cyclonedx/cyclonedx-npm@v1
with:
output-file: sbom.json
- name: Upload SBOM
uses: actions/upload-artifact@v3
with:
name: sbom
path: sbom.json
Cost Optimization
Pipeline Cost Tracking
- name: Calculate cost
run: |
# Estimate build cost
BUILD_MINUTES=$((SECONDS / 60))
COST=$(echo "$BUILD_MINUTES * 0.008" | bc)
echo "Build cost: \$$COST"
# Report to cloud watch
aws cloudwatch put-metric-data \
--metric-name BuildCost \
--namespace CI/CD \
--value $COST
Caching Strategies
# Multi-layer caching
- name: Cache Docker layers
uses: actions/cache@v3
with:
path: /tmp/.docker-cache
key: ${{ runner.os }}-docker-${{ hashFiles('Dockerfile') }}
restore-keys: |
${{ runner.os }}-docker-
- name: Cache Go modules
uses: actions/cache@v3
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache npm
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
Conclusion
Effective CI/CD requires thoughtful design, automated testing, and reliable deployment strategies. Start with basic pipelines and add complexity as needed. The goal is faster, safer releases.
2026 trends:
- GitOps as the standard for Kubernetes deployments
- Progressive delivery with canary and feature flags
- Supply chain security (SLSA, SBOM)
- Cost optimization in pipelines
- Chaos engineering integration
Resources
- GitHub Actions Docs
- GitLab CI Docs
- Argo CD Documentation
- Spinnaker
- Flux CD Documentation
- SLSA Framework
Comments