Skip to content
Documentation Background

CI/CD

Continuous Integration (CI) and Continuous Delivery (CD) form the backbone of modern software engineering. They automate the testing, validation, and deployment of code, transforming what used to be a months-long manual release cycle into a reliable process that takes minutes or hours.


Before CI/CD, software releases were manual, infrequent, and high-risk. Code sat in feature branches for weeks; integration was a painful, multi-day event; and deployments happened after-hours with a team on standby ready to roll back. The feedback loop between writing code and knowing if it worked in production was measured in days or weeks - long enough for bugs to compound and context to evaporate.

CI/CD solves this by collapsing that feedback loop into minutes:

BenefitWhat it means in practice
Faster feedbackAutomated tests and code reviews give developers immediate signal on whether their change broke anything - before it ever reaches production.
Higher reliabilityFrequent, smaller releases with automated validation reduce the blast radius of any single change, lowering the risk of downtime.
Better collaborationShared pipelines, common tooling, and enforced standards reduce conflicts and keep distributed teams aligned.
Lower costsAutomated testing catches defects early when they’re cheap to fix. Cloud-native infrastructure and containers eliminate idle capacity.

Pipelines and infrastructure are the two structural components that make this possible - the pipeline automates the work; the infrastructure provides the environment to run it in.

CI/CD has undergone a fundamental shift over the past decade - not just in tooling, but in how the discipline is conceptualized and applied. Four key transitions define this evolution:

ThenNow
Reference architectures - abstract, untested guidelinesDesign patterns - proven, reusable solutions from real implementations
Push model - a commit triggers the pipeline, which pushes changes to productionPull model (GitOps) - a controller continuously reconciles live state with the desired state in Git
Test Pyramid - unit-test heavy, designed for monolithsTest Diamond - integration-test heavy, designed for microservices
App code only - CI/CD automated the build and deploy of source codeInfrastructure as Code (IaC) - CI/CD now manages cloud infrastructure with the same rigor
  • Pipelines: The backbone of any CI/CD setup - a sequence of automated stages that take your code from commit to deployment. Each stage (build, test, package, deploy) has a clear job: catch problems early, fail fast, and keep humans out of repetitive work. Pipelines aren’t one-size-fits-all; they adapt to whatever they’re moving - source code, container images, Helm charts, or config bundles.

  • Infrastructure: Everything the pipeline runs on - servers, networks, storage, databases, containers, and cloud services. This breaks down into two layers:

    • Provisioning tools (Terraform, Ansible, Chef, Puppet) automate the creation and management of these resources so environments are reproducible and consistent.
    • Observability tools (Prometheus, Grafana, Nagios, Datadog) monitor infrastructure health, surface pipeline bottlenecks, and close the feedback loop between deployment and performance.

    Poor infrastructure choices - undersized runners, no caching, flaky test environments - are usually the first thing that makes pipelines slow and unreliable.

A CI/CD pipeline is an automated workflow that bridges the gap between writing code and delivering it to users.

  • Continuous Integration (CI): The practice of frequently merging code changes into a central repository. Each merge triggers an automated build and test suite to validate the integration.
  • Continuous Delivery (CD): The automated process of pushing the validated build to a target environment (staging or production) so users can access it.

The connection point between CI and CD is the artifact - the packaged, tested output (a Docker image, JAR file, or binary) that travels from the integration phase into delivery. Without CI/CD, developers would manually build, test, scan, and deploy code for every change - a slow, error-prone process that scales poorly.

Every CI/CD pipeline is built around the same core build cycle:

PhaseWhat happens
Source controlA push or PR triggers the pipeline via a VCS hook (Git being the default)
InitializationThe build system prepares the environment, installs dependencies, and sets up the workspace.
CompilationSource code is converted into executable code (or a distributable format for interpreted languages).
TestingAutomated tests run to validate correctness - unit, integration, and end-to-end checks.
PackagingThe tested code is assembled into a deployable artifact: a Docker image, JAR, binary, or archive.
Review / DeployThe artifact is inspected (quality gates, security scans) and promoted to a target environment.
Monitor & feedbackRuntime metrics and alerts close the loop back to the team

Pipelines come in several flavors, each suited to different workflow needs:

  • Basic pipelines: The simplest form - stages run sequentially, and all jobs within a stage run concurrently. Once every job in a stage completes, the pipeline moves to the next. A typical example: build → test → deploy. Good starting point, but limited flexibility for complex dependency graphs.

  • DAG pipelines: Instead of rigid stage ordering, a Directed Acyclic Graph (DAG) pipeline runs jobs based on their dependencies. Jobs with no dependency on each other run in parallel; those that depend on prior results wait. This makes DAG pipelines significantly faster for complex workflows - linting, unit tests, integration tests, and coverage checks can all run optimally without unnecessary serialization.

  • Merge request pipelines: Triggered only on merge requests, not every commit. Useful for running heavier checks (test suites, static analysis) specifically on proposed changes before they touch the main branch - without burning CI minutes on every push to a feature branch.

  • Merged results pipelines: A step further - these pipelines test the outcome of the merge, not just the source branch. They simulate what the codebase looks like after the merge and run checks against that, catching conflicts or regressions before they land.

  • Merge trains: Built on top of merged results pipelines, merge trains queue merge requests sequentially and test each one against the latest state of the target branch. Only one MR merges at a time, which eliminates the “works on my branch” class of integration failures at the cost of some throughput.

The right pipeline type depends on your team’s scale and tolerance for CI complexity. Most teams start with basic pipelines and graduate to DAGs or merge trains as their codebase and team size grow.

While pipelines vary based on the stack and target environment, a robust pipeline typically executes these stages in sequence:

StagePurposeExample Tools
Unit TestingTests specific functions or blocks of code in isolation (e.g., verifying add(2, 3) returns 5).JUnit, PyTest, Jest
Static Code AnalysisEnsures code is correct, properly formatted, and free of anti-patterns (e.g., unused variables).SonarQube, ESLint, Checkstyle
Vulnerability ScanningScans dependencies and application code for known security vulnerabilities (CVEs).Snyk, Trivy, Dependabot
Functional / End-to-EndValidates that new changes don’t break existing user flows or system integrations.Selenium, Cypress, Playwright
Reporting / ArtifactsStores test coverage reports, build logs, and the final compiled artifact (e.g., a Docker image or JAR).Nexus, Artifactory, JaCoCo
DeploymentThe final stage - the application is pushed to a live environment for users or QA.ArgoCD, AWS CodeDeploy

The pipeline is entirely driven by the Version Control System (VCS) - GitHub, GitLab, or Bitbucket.

Developers build features locally and push commits to the VCS. Once pushed (or when a Pull Request is opened), the VCS emits an event that triggers the CI/CD pipeline automatically.

Jenkins is the classic CI/CD orchestrator. It acts as the command center, integrating with external tools (Maven, SonarQube, Kubernetes) to execute pipeline stages.

A traditional Jenkins pipeline promotes an application progressively through environments:

  1. Dev: A minimal setup (e.g., a single EC2 instance) for initial QA and integration testing.
  2. Staging: A scaled-up replica of production to validate behavior under realistic conditions.
  3. Production: The live environment serving customer traffic.

Jenkins relies on static VMs acting as persistent worker nodes - the “pets” of infrastructure.

Modern platforms - GitHub Actions, GitLab CI/CD, CircleCI - solve the wasted compute problem by shifting to an ephemeral, containerized model.

When a repository event fires, the platform spins up a temporary Docker container (or Kubernetes pod) to run the pipeline. Once the job completes, the container is destroyed.

  • Zero Wasted Compute: You only pay for the minutes your pipeline runs. No commits = zero compute consumed.
  • Massive Scalability: The provider handles the infrastructure. Hundreds of concurrent jobs run without manual provisioning.
  • Native Events: GitHub Actions and GitLab CI/CD are built into the VCS - they understand PRs, tags, and branch rules without webhook configuration.

Orchestration is what coordinates when and how each stage runs:

ToolBest ForNotes
JenkinsSelf-hosted, customizable setupsMature ecosystem, extensive plugin support; operational overhead is real
GitLab CI/CDTeams already on GitLabPipeline config lives in the repo; tight VCS integration out of the box
GitHub ActionsGitHub-native workflowsEasy to set up; large marketplace of community actions
Travis CIOpen source projectsGitHub-integrated, simple config; less common in enterprise now
Azure DevOpsMicrosoft/enterprise environmentsFull lifecycle tooling; deep Azure integration
Argo CDKubernetes + GitOpsGit as source of truth for cluster state; pairs well with Helm and Kustomize
TektonKubernetes-native CI/CDCRD-based pipeline definitions; default in Red Hat OpenShift

There are two fundamental models for how changes move from Git to a running system:

CI/CD Push vs Pull delivery models

  • Push model (Traditional CI/CD): A code commit triggers the pipeline, which pushes the artifact directly into the target environment. Simple to reason about, but the pipeline needs credentials to access production systems.
  • Pull model (GitOps): A controller inside the cluster constantly compares the live state against the desired state declared in Git. When it detects a difference, it pulls and reconciles. No external credentials are needed in the pipeline.

GitOps is the modern evolution of this pull model, specifically for Kubernetes environments. It is more secure (credentials stay inside the cluster) and provides automatic drift detection. Tools: ArgoCD, Flux.

CI/CD isn’t just tooling - it’s an engineering discipline. The same software design patterns that structure application code also apply to how pipelines and delivery infrastructure are organized.

Design patterns are general, reusable solutions to recurring problems. They promote reusability, provide a common vocabulary, ensure maintainability, and produce adaptable systems. The three classical categories map directly into CI/CD contexts:

CI/CD Design Patterns

CategoryWhat it doesCI/CD Examples
CreationalStructured and efficient creation of pipeline objectsSingleton pipeline config, Factory for environment-specific pipelines
StructuralSimplifies how pipeline components are arranged and composedAdapter for integrating legacy tools, Composite for modular job groups
BehavioralFocuses on communication and interaction between stagesObserver for pipeline notifications, Strategy for swappable deployment algorithms

When building a CI/CD system, the Factory Method pattern can be applied to create different types of deployment pipelines - each with specific steps, tools, and configurations - without changing the pipeline orchestration logic.

The pattern works by defining an abstract PipelineCreator with a createPipeline() factory method. Concrete subclasses (DevelopmentPipelineCreator, StagingPipelineCreator, ProductionPipelineCreator) each implement their own version, returning the appropriate DeploymentPipeline instance (development, staging, or production) with the correct tooling and deployment targets pre-configured.

The result: your orchestration layer stays consistent while the environment-specific details are encapsulated in each concrete creator. Swapping out a deployment strategy for a single environment requires changing one class, not the whole pipeline definition.

The scope of CI/CD has also expanded beyond application code to include Infrastructure as Code (IaC). Modern pipelines manage cloud resources with the same rigor - using state files, pull request gates, cost analysis, golden images, and drift detection pipelines. See Pipeline as Code for the full breakdown.

The right testing strategy depends on your application architecture:

  • Test Pyramid - best for monolithic applications. Emphasizes a large base of fast, cheap unit tests, fewer integration tests, and a small number of end-to-end tests at the top.

    Test Pyramid model for monolithic applications

  • Test Diamond - best for microservices and distributed systems. Shifts emphasis to integration tests, because the communication layer between services is where failures actually occur. Unit tests remain but carry less weight individually.

    Test Diamond model for microservices architectures

How new code reaches users is one of the most consequential decisions in a CI/CD system. The three foundational strategies:

  • Rolling: Gradually replaces old instances with new ones. No duplicate infrastructure required; risk is contained to the fraction of traffic hitting the new version at any moment.

    Rolling deployment gradually shifting traffic to new version

  • Blue-Green (Red-Black): Maintains two identical production environments. The new version (Green) is deployed and fully tested before a load balancer atomically flips all traffic from Blue to Green. Zero downtime; instant rollback by flipping back.

    Blue-Green deployment with load balancer traffic switch

  • Canary: Releases the new version to a small slice of real users first. Metrics are observed before incrementally routing more traffic until 100% is on the new version - or rolling back if signals degrade.

    Canary deployment incrementally increasing traffic to new version

See Deployment Strategies for the full breakdown, including Feature Toggles, A/B Testing, and Dark Launches.

CI/CD is not a set of tools. It is a cultural and operational discipline that applies the rigor of software design patterns to the infrastructure that delivers software. When fully realized, the pipeline becomes a Software Factory - raw code goes in, tested and deployed software comes out, reliably and repeatedly.

Fully realized Software Factory model


Teams that implement CI/CD successfully share a common set of practices. These aren’t optional optimizations - they are the conditions that make the pipeline trustworthy:

PracticeWhat it looks likeWhy it matters
Branching strategyAdopt trunk-based development or a documented GitFlow variantKeeps main always deployable; reduces merge conflict risk
Automated testing on every commitUnit tests + integration tests run on each push, no exceptionsThe testing gap is where bugs survive to production
Code quality analysisRun SonarQube, CodeClimate, or Codacy as a pipeline gateCatches complexity, duplication, and security issues automatically
Peer code reviewEvery PR requires at least one reviewer before mergeCatches issues early; propagates knowledge across the team
Infrastructure as CodeProvision all environments with Terraform, Ansible, or PulumiEliminates environment drift; enables reproducible builds
Monitoring and observabilityDeploy Prometheus, Grafana, or Datadog alongside every releaseCloses the feedback loop between deployment and production health
Communication and feedbackPost pipeline results to Slack/Teams; track work in Jira or LinearKeeps stakeholders aligned and surfacing failures fast

CI/CD adoption is consistently difficult. Most organizations encounter the same set of obstacles:

ChallengeRoot causeRecommended approach
Integration complexityApplying CI/CD to legacy or large-scale projects involves untangling years of manual processesStart with a subset of the codebase; prove value incrementally before expanding
Security and compliance gapsAutomated pipelines move fast and can ship vulnerabilities just as fast as featuresIntegrate SAST, DAST, and dependency scanning as non-negotiable pipeline stages
Operational hurdlesManaging runners, caching, artifact storage, and scaling pipelines is real infrastructure workTreat the pipeline as a product; assign ownership and invest in its health
Knowledge gapsNew tools, new paradigms, and cultural change all at once overwhelms teamsInvest in training, start small, and favor simplicity over sophistication initially

Design Patterns (from CI/CD Design Patterns)

Section titled “Design Patterns (from CI/CD Design Patterns)”