Continuous Integration and Continuous Delivery (CI/CD) pipelines automate the path from code commit to production deployment. A well-designed CI/CD pipeline catches bugs early, enforces quality standards, and enables teams to deploy with confidence multiple times per day. This guide covers pipeline architecture, tool selection, testing strategies, and deployment automation — essential knowledge for DevOps and platform engineering interviews.
CI/CD Pipeline Stages
A production CI/CD pipeline has distinct stages: (1) Source — triggered by a git push or pull request. The pipeline fetches the code and determines what changed. (2) Build — compile the application, resolve dependencies, and produce build artifacts (Docker image, JAR file, binary). Build should be deterministic: the same commit always produces the same artifact. (3) Test — run unit tests, integration tests, and static analysis. Fast feedback: unit tests should complete in under 2 minutes. Integration tests run in parallel. (4) Security scan — scan dependencies for known vulnerabilities (Dependabot, Snyk), scan the Docker image for CVEs (Trivy), and run SAST (Static Application Security Testing) on the code. (5) Publish — push the build artifact to a registry (Docker registry, npm registry, Maven repository). Tag with the git SHA for traceability. (6) Deploy to staging — deploy the artifact to a staging environment. Run smoke tests and end-to-end tests against staging. (7) Deploy to production — deploy via canary, blue-green, or rolling update. Monitor for regressions. Each stage gates the next: a test failure prevents deployment. The pipeline should complete in under 15 minutes for fast feedback.
GitHub Actions vs Jenkins
GitHub Actions: cloud-hosted CI/CD integrated with GitHub. Workflows are defined in YAML files (.github/workflows/). Triggered by GitHub events (push, pull_request, schedule, workflow_dispatch). Runners: GitHub-hosted (Ubuntu, macOS, Windows — free for public repos, metered for private) or self-hosted. Marketplace: thousands of pre-built actions for common tasks (checkout, setup-node, docker/build-push-action). Best for: teams using GitHub, projects that need simple-to-moderate CI/CD without managing infrastructure. Jenkins: self-hosted, open-source automation server. Pipelines defined in Jenkinsfile (Groovy DSL). Extensible with 1800+ plugins. Supports complex workflows: parallel stages, conditional execution, matrix builds, and custom agents. Best for: organizations with complex build requirements, on-premise infrastructure, or compliance requirements that prevent cloud CI. Trade-offs: GitHub Actions is simpler to set up and maintain but less flexible for complex pipelines. Jenkins is highly customizable but requires infrastructure management (the Jenkins server, agents, plugins, upgrades). Alternatives: GitLab CI/CD (integrated with GitLab), CircleCI (cloud-hosted, fast), Buildkite (hybrid — cloud orchestration with self-hosted agents).
Testing Strategy in CI
The testing pyramid guides what to test in CI: (1) Unit tests (base of pyramid) — fast, isolated, test individual functions and classes. Run all unit tests on every commit. Target: complete in under 2 minutes. Coverage: aim for 80%+ line coverage on business logic, but do not chase 100% (diminishing returns). (2) Integration tests (middle) — test interactions between components (service to database, service to external API). Use real dependencies where possible (test containers for databases, mock servers for external APIs). Run on every PR. Target: complete in under 5 minutes. (3) End-to-end tests (top) — test complete user flows through the deployed application. Run against staging after deployment. Slower and more brittle than lower-level tests. Keep the suite small (10-20 critical user journeys). Target: complete in under 10 minutes. Parallelization: split tests across multiple CI runners. Most CI systems support matrix builds (run tests on multiple OS/language versions) and test splitting (distribute test files across runners). Flaky test management: quarantine flaky tests (track failure rate, mark as flaky, fix or delete). Flaky tests erode confidence in the pipeline — engineers start ignoring failures.
Trunk-Based Development
Trunk-based development: all developers commit to a single branch (main/trunk). Short-lived feature branches (1-2 days) are merged via pull requests with CI checks. No long-lived feature branches. Benefits: (1) Continuous integration — code is integrated daily, catching conflicts and bugs early. Long-lived branches diverge and create painful merge conflicts. (2) Smaller changes — frequent merges encourage small, reviewable PRs. Smaller changes are easier to review, test, and roll back. (3) Always releasable — the main branch is always in a deployable state. Any commit can be released to production. Feature flags hide incomplete features from users while the code is in main. Contrast with GitFlow: GitFlow uses long-lived develop and release branches. Merges are infrequent and large. Release preparation takes days. GitFlow was designed for software with infrequent releases (monthly, quarterly). Trunk-based development is designed for continuous delivery (multiple times per day). Google, Facebook, and Netflix use trunk-based development. It requires: comprehensive CI (every PR is tested), feature flags (hide incomplete work), and small PRs (incremental changes, not monolithic features).
Artifact Management
Build artifacts (Docker images, JARs, binaries) must be stored, versioned, and distributed reliably. Artifact registry types: Docker registries (Docker Hub, AWS ECR, Google Artifact Registry) for container images. Package registries (npm, PyPI, Maven Central, or private registries like Artifactory/Nexus) for library packages. Object storage (S3) for arbitrary build outputs (test reports, coverage reports, compiled assets). Tagging strategy: tag artifacts with the git commit SHA (immutable, traceable) and optionally with semantic version tags (v1.2.3) for releases. Never use mutable tags like “latest” for production deployments — you cannot roll back to “latest” because it changes. Promotion model: build the artifact once in CI. Promote the same artifact through environments (staging -> production). Never rebuild for different environments — environment-specific configuration is injected at deploy time (environment variables, config maps). This ensures the exact artifact tested in staging is what runs in production. Retention: keep production artifacts indefinitely (for rollback). Delete non-production artifacts after 30-90 days to save storage costs.
Pipeline Security
CI/CD pipelines are a high-value attack target — they have access to production credentials and deploy code. Security practices: (1) Secrets management — never store secrets in pipeline YAML or repository files. Use the CI platform secret store (GitHub Actions secrets, Jenkins credentials). Inject secrets as environment variables at runtime. Rotate secrets regularly. (2) Least privilege — pipeline service accounts should have the minimum permissions needed. A build pipeline needs read access to the code repository and write access to the artifact registry — not production database credentials. (3) Signed artifacts — sign Docker images and build artifacts. Verify signatures before deployment. Cosign (Sigstore) signs container images with keyless signing using OIDC identity. (4) Supply chain security — pin dependency versions (lock files), verify dependency checksums, and scan for malicious packages. SLSA (Supply-chain Levels for Software Artifacts) framework defines security levels for build integrity. (5) Branch protection — require PR reviews, passing CI checks, and signed commits before merging to main. Prevent force pushes to protected branches. (6) Audit logging — log all pipeline executions, who triggered them, what was deployed, and which secrets were accessed.