Skip to main content
โšก Calmops

CI/CD Pipeline Best Practices: Modern DevOps 2026

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

  1. Checkout: Get code
  2. Install: Dependencies
  3. Lint: Code style
  4. Test: Unit tests
  5. Build: Compile/bundle
  6. Security: Scan for vulnerabilities
  7. 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

Comments