CI/CD: Continuous Integration and Continuous Delivery

📖 11 min read

What is CI/CD

CI/CD is a set of practices that automate the integration, testing, and delivery of software changes, enabling teams to ship code faster, more reliably, and with higher quality.

The goal of CI/CD is to make deployments boring: routine, repeatable, and reliable.

Core Components

Continuous Integration (CI): The practice of frequently merging code changes into a shared repository, with automated builds and tests to catch integration issues early.

Continuous Delivery (CD): The practice of automatically preparing code for release to production, ensuring it’s always in a deployable state.

Continuous Deployment: An extension of continuous delivery where every change that passes automated tests is automatically deployed to production.

Why CI/CD Matters

Speed and Frequency:

  • Deploy multiple times per day instead of monthly or quarterly
  • Reduce time from code commit to production from weeks to hours
  • Enable faster feedback loops and iteration

Quality and Reliability:

  • Catch bugs earlier through automated testing
  • Reduce manual errors through automation
  • Ensure consistent build and deployment processes
  • Enable quick rollbacks when issues occur

Developer Productivity:

  • Eliminate repetitive manual tasks
  • Reduce context switching and deployment friction
  • Provide fast feedback on code quality
  • Allow developers to focus on building features

Business Value:

  • Faster time to market for features
  • Reduced risk of large, complex releases
  • Better responsiveness to market changes
  • Lower operational costs through automation

CI/CD and Technical Agreement

A pipeline is a set of agreements written down in a form that executes. That framing explains what a failing build actually tells you.

Tests are agreements encoded as executable specifications. When a test fails, an agreement was broken.

Each stage checks a different kind of agreement. Unit tests check that a component behaves the way its author said it would. Integration tests check that two components still agree about the contract between them. Contract tests check that a service and its consumers agree about an interface neither one owns alone. Security and quality gates check that the code meets standards the team agreed to before any of it was written.

This changes the useful question after a red build. “How do I make it pass” treats the check as an obstacle. “Which agreement broke” treats it as information, and the answer is often about people rather than code. A merge conflict in a file two people rewrote in the same week means they were not aligned on concurrent work. An integration test failing on a contract nobody changed means the contract was never genuinely agreed, only assumed.


Continuous Integration (CI)

Continuous Integration is the practice of automatically building and testing code every time a team member commits changes to version control.

Core CI Principles

1. Maintain a Single Source Repository

  • All code lives in version control (Git, etc.)
  • Include build scripts, tests, and configuration
  • Never rely on code that exists only on developer machines

2. Automate the Build

  • Build should run with a single command
  • No manual steps required
  • Reproducible on any machine

3. Make Your Build Self-Testing

  • Automated tests run as part of the build
  • Build fails if tests fail
  • Tests must be fast enough to run frequently

4. Everyone Commits to Mainline Every Day

  • Small, frequent commits reduce integration risk
  • Keeps branches short-lived (feature flags for incomplete work)
  • Reduces merge conflicts and integration hell

5. Every Commit Triggers a Build

  • Automated build and test on every commit
  • Fast feedback to developers (< 10 minutes ideal)
  • Clear pass/fail status

6. Keep the Build Fast

  • Optimize test suite for speed
  • Use test parallelization
  • Consider splitting into fast and slow test suites

7. Test in a Clone of Production

  • Build and test in environment matching production
  • Use containers or infrastructure-as-code for consistency
  • Avoid “works on my machine” problems

8. Make it Easy to Get Latest Deliverables

  • Latest build artifacts readily available
  • Clear versioning and tagging
  • Artifact repository for binaries

9. Everyone Can See Results

  • Build status visible to entire team
  • Radiator/dashboard showing build health
  • Notifications for failures

10. Automate Deployment

  • Push-button or automated deployment
  • Same deployment process for all environments
  • Deployment is part of the CI process

CI Workflow

Developer commits code
       ↓
Trigger CI pipeline
       ↓
Checkout code
       ↓
Install dependencies
       ↓
Build application
       ↓
Run automated tests
       ├─ Unit tests
       ├─ Integration tests
       └─ Linting/static analysis
       ↓
Generate build artifacts
       ↓
Report results to team

Benefits of CI

Early Bug Detection:

  • Find integration issues within hours, not weeks
  • Easier to debug (fewer changes to investigate)
  • Cheaper to fix (developer context still fresh)

Reduced Integration Risk:

  • Small, frequent integrations are less risky than big bang merges
  • Merge conflicts caught and resolved quickly
  • Always have a working build

Higher Code Quality:

  • Automated testing enforces quality gates
  • Code review integrated into workflow
  • Static analysis catches issues automatically

Better Collaboration:

  • Shared responsibility for build health
  • Transparency around code quality
  • Faster feedback cycles

Continuous Delivery and Deployment

Continuous Delivery

Continuous Delivery ensures code is always in a deployable state, with automated testing and deployment to staging environments. Deployment to production requires manual approval.

Key characteristics:

  • Every change passes automated tests
  • Code can be deployed to production at any time
  • Deployment is a business decision, not a technical constraint
  • Manual approval gate before production

Workflow:

Code commit → CI build → Deploy to staging → Automated tests → Ready for production
                                                                          ↓
                                                                  Manual approval
                                                                          ↓
                                                                Deploy to production

Continuous Deployment

Continuous Deployment takes continuous delivery one step further: every change that passes automated tests is automatically deployed to production without manual intervention.

Key characteristics:

  • Fully automated pipeline from commit to production
  • No manual approval gates
  • Requires high confidence in automated testing
  • Fast feedback from real users

Workflow:

Code commit → CI build → Automated tests → Deploy to staging → More tests → Auto-deploy to production

CD vs. Continuous Deployment

Continuous Delivery

  • Manual approval required for production
  • Release frequency: as needed (on-demand)
  • Lower risk tolerance with manual gate
  • Moderate feedback speed
  • Moderate maturity required

Continuous Deployment

  • Fully automated production deployment
  • Release frequency: every successful build
  • Higher risk tolerance required
  • Very fast feedback from real users
  • High maturity required

Prerequisites for CD/Continuous Deployment

Robust Automated Testing:

  • Comprehensive test suite covering critical paths
  • High confidence in test accuracy (low false positives/negatives)
  • Fast test execution

Deployment Automation:

  • Infrastructure as Code (IaC)
  • Automated provisioning and configuration
  • Consistent deployment across environments

Monitoring and Observability:

  • Real-time monitoring of application health
  • Automated alerting on anomalies
  • Fast rollback capabilities

Feature Flags:

  • Decouple deployment from release
  • Gradual rollouts and A/B testing
  • Quick feature toggles for issues

Culture of Quality:

  • Shared responsibility for production
  • Blameless post-mortems
  • Investment in testing and automation

Pipeline Architecture

Pipeline Stages

A typical CI/CD pipeline consists of multiple stages that code must pass through before reaching production.

Stage 1: Source

  • Triggered by code commit or pull request
  • Fetch code from version control
  • Determine what changed

Stage 2: Build

  • Compile code
  • Resolve dependencies
  • Create build artifacts
  • Run fast validation checks

Stage 3: Test

  • Unit tests
  • Integration tests
  • Static analysis and linting
  • Code coverage analysis

Stage 4: Security Scan

  • SAST (Static Application Security Testing)
  • Dependency scanning (SCA)
  • Secret detection
  • License compliance checks

Stage 5: Package

  • Create deployable artifacts
  • Build container images
  • Version and tag artifacts
  • Push to artifact repository

Stage 6: Deploy to Staging

  • Deploy to staging environment
  • Run smoke tests
  • Dynamic security testing (DAST)
  • Performance testing

Stage 7: Deploy to Production

  • Manual approval (Continuous Delivery) or automatic (Continuous Deployment)
  • Deployment strategy (blue-green, canary, rolling)
  • Production smoke tests
  • Monitor for issues

Stage 8: Post-Deployment

  • Verify deployment success
  • Monitor application health
  • Collect metrics
  • Alert on anomalies

Pipeline Design Principles

1. Pipeline as Code

  • Store pipeline definitions in version control
  • Use declarative configuration (YAML, JSON)
  • Enable code review of pipeline changes
  • Version and track pipeline evolution

2. Fast Feedback

  • Run fastest tests first
  • Fail fast on critical issues
  • Provide clear error messages
  • Notify developers immediately

3. Idempotent and Reproducible

  • Same inputs produce same outputs
  • No manual steps required
  • Isolated from external state
  • Use fixed versions for dependencies

4. Environment Parity

  • Consistent environments across pipeline
  • Use containers or IaC for reproducibility
  • Minimize dev/prod differences

5. Automated Rollback

  • Quick rollback mechanism
  • Tested rollback procedures
  • Preserve previous versions
  • Automated health checks trigger rollback

6. Security by Default

  • Security scanning integrated, not optional
  • Fail builds on critical vulnerabilities
  • Least privilege for pipeline permissions
  • Audit all pipeline activities

Pipeline Patterns

The four common patterns differ in shape: one line of stages, stages that fan out and join, deployments that fan out to several targets, and different pipelines for different branches.

Flow

Four Pipeline Patterns

Sequential, parallel, fan-out and branch-based pipeline shapes.

Four CI/CD pipeline patterns shown as small flow diagrams Sequential: build, test, package, deploy and verify run one after another. Parallel: after build, unit tests, integration tests, a security scan and linting run at once, then join into package and deploy. Fan-out and fan-in: a package deploys to regions A, B and C at the same time, the results are aggregated, then verified. Branch-based: a feature branch runs a PR pipeline of build, test and security scan; the main branch runs the full pipeline including deployment to staging; a release tag runs the production pipeline that deploys to production. SEQUENTIAL PARALLEL FAN-OUT / FAN-IN BRANCH-BASED Build Test Package Deploy Verify One stage after another. Simple to follow and debug. Build Unit tests Integration tests Security scan Linting Package Deploy Independent checks run at once. Faster, more resources. Package Deploy to region A Deploy to region B Deploy to region C Aggregate results Verify Deploy to several targets at once, then collect the results. Feature branch PR pipeline: build, test, security scan Main branch Full pipeline: build, test, scan, staging Release tag Production pipeline: deploy to prod More validation as code moves toward production.

Sequential Pipeline:

  • Simple and predictable
  • Stages run one after another
  • Easy to understand and debug

Parallel Pipeline:

  • Faster execution
  • Independent stages run concurrently
  • Requires more resources

Fan-Out/Fan-In Pipeline:

  • Deploy to multiple targets simultaneously
  • Collect and aggregate results
  • Useful for multi-region deployments

Branch-Based Pipeline:

  • Different pipeline behavior per branch
  • More thorough validation on main branch
  • Production deployments from release tags

Automated Testing in Pipelines

Where Each Test Type Belongs in the Pipeline

The testing pyramid describes the shape a test suite should have: many fast unit tests, fewer integration tests, and a small number of slow end-to-end tests. The proportions often quoted alongside it are a heuristic rather than a measured result, and the shape matters more than any particular split.

What a pipeline adds to that is placement. A test’s speed and its blast radius decide which stage it belongs in, and getting this wrong is the most common reason pipelines become slow enough that people route around them.

Test type Typical duration Where it runs What blocks on it
Unit Milliseconds Every commit, before anything else The build
Integration Seconds Every commit, after unit tests pass The build
Contract Seconds Every commit for the providing service The build, and the consumer’s build
End-to-end Minutes After deployment to a test environment Promotion, not the build
Performance Minutes to hours Scheduled, or before a release A release decision, by human judgement
Security scanning Varies by class Several stages, each class where it can run Depends on severity policy

The ordering principle is to fail as early and as cheaply as possible. Anything that can run before a build should, anything that needs an artifact runs after it, and anything that needs a running system runs after deployment to somewhere that is not production.

The other rule is that only fast, reliable checks should gate a merge. A slow test in the commit stage is a tax on every change, and a flaky one in the commit stage teaches the team to re-run rather than to read.

Test Types in CI/CD

Unit Tests:

  • Run first in pipeline (fast feedback)
  • Should complete in < 5 minutes
  • High test coverage (80%+ is common)
  • Mock external dependencies

Integration Tests:

  • Test API contracts
  • Database interactions
  • Third-party service integrations (with mocking)
  • Should complete in < 15 minutes

Contract Tests:

  • Verify API contracts between services
  • Producer and consumer tests
  • Tools: Pact, Spring Cloud Contract
  • Prevent breaking changes

Smoke Tests:

  • Quick validation after deployment
  • Test critical user paths
  • Verify app is running and accessible
  • Run in < 5 minutes

Performance Tests:

  • Load testing
  • Stress testing
  • Benchmark comparisons
  • Run on staging before production

Security Tests:

  • Static analysis (SAST)
  • Dynamic analysis (DAST)
  • Dependency scanning (SCA)
  • Penetration testing (periodic)

Test Strategies

Shift-Left Testing:

  • Run tests as early as possible
  • Developers run tests locally before commit
  • Fast feedback reduces context switching
  • Catch issues before they reach CI

Parallel Test Execution:

  • Split tests across multiple workers
  • Dramatically reduce test execution time
  • Tools: Pytest-xdist, Jest parallel, TestNG parallel

Flaky Test Management

  • Identify and quarantine: Track which tests fail intermittently
  • Track trends: Monitor flaky test rates over time
  • Fix or remove: Don't let flaky tests erode confidence
  • Never ignore: Flaky tests lead to ignored test suites

Test Data Management:

  • Use factories/fixtures for test data
  • Database seeding for integration tests
  • Isolate test data (separate DB per test run)
  • Clean up after tests

What Makes a Pipeline Trusted

A pipeline only does its job while people believe it. Everything above is in service of that, and the failures to watch for are the ones that erode belief rather than the ones that break the build.

Speed. A commit stage that takes longer than about ten minutes stops being feedback and becomes an interruption, and developers start batching changes to avoid it. Batching larger changes is precisely what continuous integration exists to prevent, so a slow pipeline undoes the practice it implements.

Determinism. A pipeline that fails intermittently teaches people to re-run it. Once re-running is the reflex, a genuine failure gets re-run too, and the pipeline has stopped catching anything. Treat flaky tests as outages rather than as annoyances.

Reproducibility. The artifact tested should be the artifact deployed. Rebuilding per environment means the thing verified is not the thing shipped, and the difference will eventually matter.

Reversibility. The value of deploying often comes from being able to undo it cheaply. A pipeline that can deploy in four minutes and needs two hours to roll back has automated the risky direction only.

A pipeline with those four properties is one teams will put their production changes through. Without them, the pipeline gets worked around, and the workarounds are where the incidents come from.

Found this guide helpful? Share it with your team:

Share on LinkedIn