CI/CD (Continuous Integration / Continuous Delivery) is a set of practices that automate building, testing, and deploying code changes, enabling teams to ship software reliably and frequently. CI focuses on integrating code changes; CD focuses on getting those changes to production.
Continuous Integration (CI)
CI means every code change pushed to a shared repository triggers an automated build and test pipeline. The goal: catch bugs immediately, not days later when someone tries to merge.
A typical CI pipeline:
- Developer pushes code to a branch
- CI server detects the change
- Dependencies are installed
- Code is compiled/built
- Unit tests, integration tests, and linting run
- Results are reported back (pass/fail)
If any step fails, the team knows within minutes. This keeps the main branch in a deployable state at all times.
Continuous Delivery vs. Continuous Deployment
These terms are often confused:
- Continuous Delivery: Every change that passes the pipeline can be deployed to production with a manual approval step. The code is always release-ready.
- Continuous Deployment: Every change that passes the pipeline is automatically deployed to production. No human gate.
Most teams start with Continuous Delivery and move toward Continuous Deployment as their test suites and monitoring mature.
Pipeline configuration
Most CI/CD systems use YAML configuration files:
# GitHub Actions example
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
- run: npm run build
Popular CI/CD platforms
- GitHub Actions: Integrated into GitHub, uses YAML workflow files
- GitLab CI/CD: Built into GitLab, uses
.gitlab-ci.yml - Jenkins: Self-hosted, highly customizable, large plugin ecosystem
- CircleCI: Cloud-based with a generous free tier
- Buildkite: Runs agents on your own infrastructure
Best practices
- Keep pipelines fast: Developers won’t wait 45 minutes for feedback. Parallelize tests, cache dependencies, use incremental builds.
- Test what matters: Unit tests for logic, integration tests for boundaries, end-to-end tests for critical paths. The testing pyramid applies.
- Fail fast: Put the quickest checks (linting, type checking) first.
- Infrastructure as code: Define your pipeline in version control alongside the application code.
- Immutable artifacts: Build once, deploy the same artifact to every environment.
Generate GitHub Actions workflows with the GitHub Actions Generator and format pipeline YAML with the YAML Formatter.