4 min read
On this page

Continuous Integration and Continuous Deployment

CI/CD pipeline stages — from commit to production

CI/CD automates the build, test, and deployment pipeline — enabling rapid, reliable software delivery.

Continuous Integration (CI)

Developers integrate code frequently (at least daily). Each integration is verified by automated builds and tests.

CI Pipeline

Developer pushes code
    ↓
[Build] → Compile, resolve dependencies
    ↓
[Lint] → Code style, static analysis
    ↓
[Unit Tests] → Fast tests (~seconds)
    ↓
[Integration Tests] → Component interaction tests (~minutes)
    ↓
[Security Scan] → Dependency vulnerabilities, SAST
    ↓
[Artifact] → Build deployable artifact (binary, container image)
    ↓
Report: ✅ All checks passed / ❌ Failure (block merge)

CI Best Practices

  1. Keep the build fast (< 10 minutes). Parallelize tests.
  2. Fix broken builds immediately. A broken main branch blocks everyone.
  3. Run on every push/PR. No exceptions.
  4. Automate everything. No manual build steps.
  5. Make build results visible. Dashboard, Slack notifications.

CI Systems

| System | Type | Key Feature | |---|---|---| | GitHub Actions | Cloud-hosted | Integrated with GitHub. YAML workflows | | GitLab CI | Self-hosted/Cloud | Built into GitLab. Auto DevOps | | Jenkins | Self-hosted | Most flexible. Plugin ecosystem. Groovy DSL | | CircleCI | Cloud-hosted | Docker-first. Orbs (reusable configs) | | Buildkite | Hybrid | Cloud orchestration, self-hosted agents |

GitHub Actions Example

name: CI
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Build
        run: cargo build --release

      - name: Test
        run: cargo test

      - name: Lint
        run: cargo clippy -- -D warnings

      - name: Format check
        run: cargo fmt -- --check

Continuous Delivery

Code is always in a deployable state. Deployment to production requires manual approval.

CI Pipeline → [Staging Deploy] → [Manual Approval] → [Production Deploy]

Continuous Deployment

Every change that passes CI is automatically deployed to production. No manual approval.

CI Pipeline → [Staging Deploy] → [Automated Tests] → [Production Deploy]

Requires: High confidence in automated tests. Feature flags. Fast rollback capability.

Pipeline Design

Stages

[Build] → [Test] → [Security] → [Staging] → [Approval] → [Production]

Fast feedback: Run fast checks first (lint, compile, unit tests). Slow checks later (integration, E2E, security).

Fail fast: Stop the pipeline on first failure. Don't waste time running later stages.

Artifact Management

Build once, deploy many times. Don't rebuild for each environment.

Build → artifact (container image, binary) → store in registry
  ↓ deploy to staging (same artifact)
  ↓ deploy to production (same artifact)

Artifact registries: Docker Hub, GitHub Container Registry, AWS ECR, Nexus, Artifactory.

Environment Promotion

Development → Staging → Production

Each environment:
- Same artifact (different config)
- Environment-specific secrets (injected at deploy time)
- Increasing rigor (manual testing in staging, canary in production)

Deployment Strategies

Blue-Green Deployment

Run two identical environments. One serves traffic (blue), the other is idle (green).

1. Green environment running v1 (serving traffic)
2. Deploy v2 to Blue environment
3. Test v2 on Blue
4. Switch router to Blue (now serving traffic)
5. Green becomes idle (rollback target)

Rollback: Switch back to Green (instant).

Cost: Two full environments. But rollback is instant and reliable.

Canary Deployment

Gradually route traffic to the new version. Monitor for errors.

1. Deploy v2 to a subset (1% of traffic → canary)
2. Monitor: error rate, latency, business metrics
3. If OK: increase to 10%, then 50%, then 100%
4. If problems: roll back canary to v1

Advantages: Low risk. Real user traffic validates the new version. Can catch issues that tests miss.

Rolling Deployment

Update instances one at a time (or in small batches).

[v1][v1][v1][v1]  → [v2][v1][v1][v1] → [v2][v2][v1][v1] → [v2][v2][v2][v2]

Kubernetes default: Rolling update with configurable maxSurge and maxUnavailable.

Rollback: Deploy v1 again (another rolling update). Slower than blue-green rollback.

Feature Flags

Decouple deployment from release. Deploy code with features hidden behind flags. Enable features gradually.

IF FEATURE_FLAGS.IS_ENABLED("new_search", user)
    NEW_SEARCH_ALGORITHM(query)
ELSE
    OLD_SEARCH_ALGORITHM(query)

Use cases: Canary releases (enable for 1% of users), A/B testing, kill switches (disable broken feature without deploy), trunk-based development.

Tools: LaunchDarkly, Unleash, Flagsmith, environment variables (simplest).

Infrastructure as Code (IaC)

Define infrastructure (servers, networks, databases) in code. Version controlled. Reproducible.

Terraform

resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "t3.micro"
  tags = { Name = "web-server" }
}

resource "aws_db_instance" "db" {
  engine         = "postgres"
  instance_class = "db.t3.micro"
  allocated_storage = 20
}

Declarative: Describe desired state. Terraform computes and applies the diff.

State: Terraform tracks current infrastructure state. terraform plan shows what will change. terraform apply makes changes.

Pulumi

IaC using general-purpose languages (TypeScript, Python, Go, Rust).

const server = new aws.ec2.Instance("web", {
    ami: "ami-0c55b159cbfafe1f0",
    instanceType: "t3.micro",
});

GitOps

Use Git as the single source of truth for infrastructure and application state.

Git repo (desired state) → GitOps controller → Kubernetes cluster (actual state)
                              ↕ reconcile
                        (ArgoCD / Flux)

ArgoCD: Monitors a Git repo. Automatically syncs Kubernetes resources to match the repo. Detects drift.

Flux: Similar to ArgoCD. Pull-based. Lightweight.

Benefits: Audit trail (git log). Rollback (git revert). Review (PRs for infra changes). Reproducible.

Pipeline Security

Static Analysis Security Testing (SAST)

Analyze source code for vulnerabilities without executing it.

Tools: Semgrep, SonarQube, CodeQL (GitHub), Clippy (Rust — catches some security issues).

Software Composition Analysis (SCA)

Scan dependencies for known vulnerabilities.

cargo audit              # Rust: check for known vulnerabilities in dependencies
npm audit                # Node.js

Tools: Dependabot (GitHub), Snyk, Trivy, cargo-audit.

Container Scanning

Scan container images for vulnerabilities in base images and installed packages.

Tools: Trivy, Grype, Docker Scout, Snyk Container.

Secrets Scanning

Detect accidentally committed secrets (API keys, passwords, tokens).

Tools: git-secrets, Gitleaks, TruffleHog, GitHub secret scanning.

Monitoring and Feedback

Deployment Metrics

  • Deployment frequency: How often you deploy. (Elite: multiple per day.)
  • Lead time for changes: Time from commit to production. (Elite: < 1 hour.)
  • Change failure rate: Percentage of deployments causing incidents. (Elite: < 5%.)
  • Mean time to recovery (MTTR): Time to recover from a failure. (Elite: < 1 hour.)

These four metrics (from DORA research) measure DevOps performance.

Applications in CS

  • Every software project: CI/CD is table stakes for professional software development.
  • Microservices: Independent CI/CD pipelines per service. Deployment autonomy.
  • Mobile: CI builds for iOS/Android. Beta distribution (TestFlight, Firebase App Distribution).
  • Infrastructure: IaC tested in CI. GitOps for deployment. Immutable infrastructure.
  • Data pipelines: CI for data transformation code. CD for pipeline deployment.