← Back to Blog

What Is a CI/CD Pipeline? What Does a Complete One Look Like?

CI/CD stands for Continuous Integration and Continuous Delivery/Deployment. Put simply, it's a process that makes software development faster, more stable, and less error-prone.

What is CI/CD?

In the CI (Continuous Integration) step, every time a developer pushes new code to Git, the system automatically checks whether that code has issues (e.g. running unit tests, checking code style, doing a trial build...). This lets you catch bugs early and fix them while they're still easy.

Once the code is solid, comes the CD part. Depending on how a team is organized, CD can mean:

Continuous Delivery: the code is packaged and ready to deploy, but still needs someone to hit a button to push it to the real environment.

Continuous Deployment: the code gets automatically deployed to the real environment, no manual step needed.

Thanks to CI/CD, you save a lot of time on testing and deploying software, while reducing the risk of human error from doing it manually.

In this post I'll share what a "complete" real-world CI/CD flow looks like, based on my own experience as a DevOps Engineer.

CI/CD pipeline overview diagram

1. Continuous Integration

1.1 Code Checkout / Clone

Before starting any CI/CD process, we need to pull/clone the source code from a repository like GitHub or GitLab so we can build a finished version or run tests on the source code.

CategoryCommon tools
Source ControlGitHub, GitLab, Bitbucket, Azure Repos, Gitea,...

1.2 Build / Install dependencies

Installing dependencies downloads and installs third-party libraries, dependent packages, or related install artifacts that you can't see directly in the source code without running the install step.

This step is necessary for the subsequent analysis steps to run effectively. Depending on whether your application runs as a container and which language it's written in, you can pick the appropriate tool.

CategoryCommon tools
Per-language build toolsMaven, Gradle, sbt, npm/yarn/pnpm, pip/Poetry, Cargo, Go modules, Bazel, Buck, CMake, Make, MSBuild, dotnet CLI
Container & image buildDocker CLI/BuildKit, Buildah, Kaniko, Podman, Jib, ko, img, buildpacks (Paketo, Cloud Native Buildpacks)

1.3 Static Code Analysis

When a development team works together, a lot of code gets written every day. Even when everyone tries to write good code, humans still make mistakes, especially small ones that are hard to spot right away.

Static Code Analysis is where you use tools to automatically check the source code without running the application. It's like spell-checking a document before sending an email — except here you're checking for bugs in code.

The input for this step is the source code and container image from the two steps above.
These analysis steps can run in parallel to save time.

Type of analysisCommon toolsPurpose
Lint / style checkESLint, Pylint, Flake8, RuboCop, Prettier, Stylelint, go vet, golangci-lintChecks syntax errors, standardizes code per convention, makes code easier to read and maintain
Unit testing & coverageJUnit, TestNG, pytest, unittest, jest, mocha, RSpec, go test, NUnit, xUnitMakes sure functions/modules work as expected and measures test coverage
Code coverage reportingJaCoCo, Istanbul/nyc, Coverlet, Codecov, SonarCloud, CloverVisualizes which parts of the source code have been tested, helping improve test quality
Static application security testing (SAST)SonarQube, Semgrep, Checkmarx, Veracode, GitHub CodeQL, Fortify, CoverityScans source code to catch potential security vulnerabilities before the app is built or run
Secrets detectionTruffleHog, Gitleaks, Detect-Secrets, GitGuardianDetects sensitive info (API keys, passwords...) leaked in the source code
License & SCA (open-source risk)Snyk, Black Duck, WhiteSource, Mend (formerly WhiteSource), FOSSA, OWASP Dependency-CheckChecks that open-source libraries use valid licenses and contain no security vulnerabilities
Container scanningTrivy, Grype, Clair, Anchore, Snyk, Docker Scout, Aqua, Prisma CloudAnalyzes the container image to detect security vulnerabilities, outdated libraries, or unsafe configuration

1.4 Build Artifact Packaging & Signing

Once the steps above pass with good results and no serious issues, we move to the next step: packaging the application and pushing it to a repository.

The following steps need to run sequentially.

StepPurposeCommon toolsPlain explanation
1. Package binariesPackage the built source code into a standard format for use or deploymentJAR, WAR, NuGet, PyPI wheels, OCI images, Helm charts, Debian/RPM, container SBOMOnce built, the code gets packaged into files a system or user can use. Depending on language/environment, the format might be a JAR (Java), a PyPI package (Python), a Docker image, a Helm chart (K8s), etc. A container SBOM is the list of components in the image, used for security checks.
2. Artifact/Image signing & provenance (optional)Digitally sign and verify provenance (who created it, was it tampered with) to guarantee the package's trustworthiness and safetycosign, Notary v2, JReleaser, GPG, AWS KMS, HashiCorp VaultLike signing or stamping a package — a digital signature confirms this artifact is "genuine" and hasn't been tampered with. If something goes wrong later, you can trace exactly who built it and whether it was interfered with.
3. Push artifactsPush the packaged, signed artifact/image to a repository so other environments (dev, staging, prod) can access and deploy itJFrog Artifactory, Sonatype Nexus, GitHub Packages, GitLab Package Registry, Azure Artifacts, S3/GCS buckets, Docker Hub, ECR, GCR, OCI-compatible registriesThe artifact gets uploaded to a "storehouse" (registry or artifact store) so others (or the CI/CD system) can pull it down. Example: uploading a Docker image to Docker Hub, a Helm chart to a GCS bucket, or a .jar to GitHub Packages. This is the bridge between the build process and the real deployment environment.

At this point, we've essentially finished the CI - Continuous Integration step.

2. Continuous Delivery / Deployment

2.1 Fetch Build Artifact

Starting the CD step, we need to download or fetch information about the Build Artifact produced at the end of the CI process.

Downloading the image/executable lets us deploy the application to different environments in the following steps.

2.2 Deploy non-production environment

In practice, we'll always have test environments like Dev, Staging, UAT, Preprod, so stakeholders can check whether the application behaves as expected.

As a DevOps Engineer, we can integrate tools to automate testing on these environments.

Depending on which deployment strategy you want to use, you'll have different tool choices.

Deployment strategyTools
Helm package & releaseHelm, Helmfile, Reckoner
Progressive delivery (GitOps)Argo CD, Flux CD, Jenkins X, Google Config Sync, Spinnaker, Harness CD
Blue/Green & canaryFlagger, Argo Rollouts, Spinnaker, Istio, Linkerd, AWS CodeDeploy
Serverless deployServerless Framework, AWS SAM, Terraform, Pulumi, SST
Mobile / desktop releaseFastlane, Microsoft App Center, Google Play Console pipelines
Feature flag rolloutLaunchDarkly, Flagsmith, Unleash, Split, CloudBees Rollout

2.3 Run tests/scans on non-production environments

Running tests on non-production environments can happen multiple times across multiple environments.

Depending on the type of application, you need to pick the appropriate types of tests — you don't necessarily need to run every single one of these.

Test typeCommon toolsPurpose
Browser / UI TestingSelenium WebDriver, Cypress, Playwright, TestCafe, PuppeteerSimulates a user interacting in the browser to test the interface and flow of a web app
Mobile App TestingAppium, Detox, EspressoTests the functionality and UI of a mobile app (Android/iOS) like a real user
Load & Performance TestingJMeter, Gatling, Locust, K6, Artillery, VegetaMeasures app performance under many concurrent users, finds bottlenecks, measures response time
Chaos / Resiliency TestingGremlin, ChaosMesh, LitmusChaos, PowerfulSealTests the system's fault tolerance by simulating failures (network loss, service death...)
Black-box DAST (Security)OWASP ZAP, Burp Suite Pro, Arachni, Netsparker, Tenable Web App ScanScans a running app for security holes (like an external hacker would), finding things like XSS, SQLi...
Fuzz Testing (Security)Jazzer, go-fuzz, libFuzzer, AFL++, DefensicsFeeds random data into the app to find crashes or unknown security bugs (zero-day)
Container Image ScanningTrivy, Grype, Anchore Engine, Clair, Aqua MicroscannerChecks the Docker image for libraries with vulnerabilities or unsafe configuration

2.4 Deploy production environment

After tests and security scans run directly on the non-production environment with no issues, we can move to the next step and deploy to production for real users.

The tools for deploying to production are typically the same as for non-production.

Deploying to production should require manual approval to avoid incidents.

2.5 Post-Deployment Validation

Deploying to production isn't the end of the CI/CD pipeline. Once again, after the production deploy completes with no issues, we need to re-run or add a few more types of tests to make sure nothing that worked fine in non-production breaks in production.

Test typeTools
Smoke / sanity tests in target envBats-core, newman, curl + bash scripts, canary synthetic checks
Contract & health checksSpring Boot Actuator, Kubernetes readiness/liveness probes

2.6 Rollback & Disaster Recovery

This is a step a lot of people forget. It lets you roll the application back to the immediately previous version when the new one has issues. The tool used for rollback is usually the same tool you use to deploy the application.

TaskCommon tools
Automatic rollback on incidentArgo Rollouts, Spinnaker, Harness, Kubernetes Deployments with health-checks
Disaster RecoveryVelero, AWS Backup, Azure Backup, Google Cloud Backup & DR

2.7 Notify & Document Release

In this final step, we need to send out a notification about the new version's status and what changed, to stakeholders — that could be the dev team, customers, PMs, and so on.

PurposeTools
Auto-generated docsmkdocs-material, Docusaurus, Sphinx, Swagger/OpenAPI, Storybook
Release notes & changelogRelease Drafter, auto-changelog, GitHub Releases, Keep a Changelog
ChatOps notificationsSlack Apps/Bot, Microsoft Teams, Mattermost, Discord webhooks

Infrastructure pipeline (Bonus)

Besides pipelines for deploying the application, there are also pipelines for provisioning the infrastructure that runs the application. Here are a few tools worth knowing for this infrastructure-provisioning pipeline:

Infrastructure-as-Code (IaC) & Policy Scan

FormatValidators / linters
Terraformterraform validate, TFLint, tfsec, Checkov, Terrascan, Infracost, Regula
Kubernetes manifestskube-score, kube-lint, Kubeval, Kube-No-Trouble, Polaris, OPA Conftest
Helm chartshelm lint, chart-testing, Datree
CloudFormationcfn-lint, CFN-Guard
Packerpacker validate, Packer Inspector

Environment Provisioning & Configuration

Automation layerPopular tools
Provision infraTerraform, Pulumi, Crossplane, AWS CloudFormation, Azure Bicep, Google Cloud Config Connector
Configuration managementAnsible, Chef, Puppet, SaltStack
Container platform bootstrappingKubernetes (kops, kube-adm), EKS-Blueprints, Rancher, OpenShift, K3s, Kind for testing
Secrets & parameter storeHashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Sealed Secrets, Doppler

Wrapping up

That covers the main steps in a typical CI/CD pipeline today, along with the tools you can use at each one. Hope this gives you a good overview of what a CI/CD pipeline needs.

If this post was useful, feel free to Follow and Upvote to support me. Thanks! ❤️

If you're running into technical challenges, need help with systems, DevOps tools, or career direction, I'm confident I can help. Get in touch at hoangviet.io.vn — I'd love to chat and collaborate with you.

Have thoughts on this?

I'd love to hear your perspective. Send me a message.

Get in touch