CI/CD Pipelines
Continuous Integration (CI): every change goes through automated build and test.
Continuous Delivery (CD): the code is always ready to ship.
Continuous Deployment: shipping happens automatically.
Modern software teams almost universally use CI/CD. The patterns and tools have stabilized; the differences are operational.
The stages
A typical pipeline:
1. **Trigger**: code push, PR, schedule, manual
2. **Build**: compile, package; produce artifact
3. **Unit test**: fast tests, run in parallel
4. **Static analysis**: linting, type checking, security scanning
5. **Integration test**: tests with real dependencies (DB, cache)
6. **Artifact storage**: the build output, versioned
7. **Deploy to environments**: dev → staging → production
8. **Post-deploy verification**: smoke tests, canary checks
Each stage gates the next. Failures stop the pipeline.
Pipeline-as-code
Modern CI/CD: pipelines defined in code, in the repo:
```yaml
.github/workflows/build.yml
name: Build and test
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./mvnw clean test
```
Pros: version controlled with the code; reviewable; reproducible.
Cons: tied to the CI tool's syntax.
GitHub Actions, GitLab CI, CircleCI, Jenkins (declarative pipelines) all support this. Avoid older "configure in UI" CI systems.
Deployment strategies
Direct (push and pray)
Just deploy. Simplest. Risky for production.
Rolling
Replace instances one at a time. New version coexists with old briefly.
Blue-green
Two identical environments. Switch traffic from blue to green. Easy rollback.
Canary
Route a small percentage of traffic to the new version. Monitor; expand or roll back.
Feature flags
Deploy code; turn features on/off via flag. Decouple deploy from release. See [FeatureToggleManagement](FeatureToggleManagement).
For most production systems, canary or blue-green are the standards. Feature flags add another layer of control.
The CI tools
GitHub Actions
The most popular. Well-integrated with GitHub. YAML pipeline definition; reusable workflows.
GitLab CI
Tightly integrated with GitLab. Mature; full-featured.
CircleCI
Independent CI. Fast; good developer experience.
Jenkins
Old school but still common. More flexible than the others; more operational overhead.
Buildkite
Hybrid (orchestrator in cloud; agents you run). Popular for performance-sensitive needs.
Cloud-native
AWS CodePipeline, GCP Cloud Build, Azure Pipelines. Useful when integrated with cloud workflows.
For most teams, GitHub Actions if on GitHub, GitLab CI if on GitLab. The tool matters less than the pipeline design.
Patterns that scale
Fast feedback
Developers want their tests to fail fast. Order stages by speed:
- Lint first (seconds)
- Unit tests next (minutes)
- Integration tests (slower)
- Deploy stages (slowest)
Failure at lint stage takes seconds; failure at deploy takes much longer.
Parallelization
Independent test groups run in parallel. Reduces total time dramatically.
Caching
Build dependencies (npm packages, Maven .m2, Docker layers). Don't re-download every build.
Matrix builds
Test against multiple versions (Node 18, 20, 22; Linux + macOS). Run matrix in parallel.
Incremental builds
Only rebuild what changed. Modern monorepo tools (Nx, Turborepo, Bazel) support this. See [MonorepoVsPolyrepo](MonorepoVsPolyrepo).
Required checks
Pipeline must pass before merge. Enforced via branch protection.
Deployment automation
Production deploys triggered by main branch merges (or manual approvals for sensitive systems). The path from code to production is automatic.
Specific patterns
Versioned artifacts
Build artifacts tagged with git SHA or semver. Same artifact deployed to dev, staging, prod — no rebuild between environments.
Environment promotion
Deploy to dev → run tests → promote to staging → run more tests → promote to production. Same artifact through the chain.
Rollback as fast as deploy
Rolling back to the previous version should be a single command or button click. Slow rollback compounds incidents.
Smoke tests post-deploy
Quick health checks after deploy: is the service responding? Can it connect to dependencies? Catches some failures before they affect users.
Canary analysis
Automatic comparison of canary metrics to baseline. If canary error rate is higher, auto-rollback. Manual canaries are slow.
Common failure patterns
- **Slow pipelines.** Developers wait; productivity drops.
- **Flaky tests in CI.** Trust erodes; failures get ignored.
- **Different paths per environment.** "Works in dev; broken in prod."
- **No rollback capability.** Disasters compound.
- **Manual deployment steps.** Human error; slow.
- **No staged rollouts.** Bad deploys hit all users.
- **CI runs only on schedule, not per-commit.** Slow feedback.
Further Reading
- [DevOpsFundamentals](DevOpsFundamentals) — Broader practice
- [TrunkBasedDevelopment](TrunkBasedDevelopment) — Branching strategy
- [ReleaseEngineering](ReleaseEngineering) — Adjacent practice
- [FeatureToggleManagement](FeatureToggleManagement) — Flags as control
- [DevOpsAndSre Hub](DevOpsAndSreHub) — Cluster index