Overview
CI/CD is the practice of automating the steps between a code change and its delivery to users. Continuous integration (CI) builds and tests every change automatically; continuous delivery (CD) automates the path from a merged change to a deployed one. This page explains what a CI/CD pipeline provides, how to keep CI fast, how to automate releases, how to notify pipeline results, and how to treat metrics such as deployment frequency. Examples use GitHub Actions, but the principles apply to any CI service.What a CI/CD pipeline provides
With a pipeline in place, the routine steps around every change run without manual intervention:Automated test gates
Tests run on every pull request, and merging is blocked until they pass. A
broken change is caught in minutes, not after release.
Automated deploys
Each environment deploys on a defined trigger — for example, staging when a
pull request targets a release branch, production when it merges.
Keep CI fast
The duration of CI is a cost paid by every change. As a guideline, keep the pipeline that gates pull requests within about 5–10 minutes.Cache dependencies
Installing dependencies from scratch on every run is pure overhead. Official setup actions can cache them keyed on the lockfile, so the cache is rebuilt only when dependencies actually change.Skip runs that cannot affect the result
A change that does not affect runtime behavior does not need the full test suite.paths-ignore skips the workflow when only the listed files changed:
If the skipped workflow is a required status check, the check stays pending
and blocks the merge. In that case, keep the workflow running and skip only
the expensive jobs inside it, or adjust the required checks.
Parallelize tests
As the test suite grows, total execution time grows with it. Parallelization splits the suite across jobs that run concurrently. Thematrix strategy runs the same job once per listed value; each job uses
its value to execute only its own slice of the suite:
Scale up runners
Once caching and parallelization are in place, larger runners with more CPU cores and memory shorten builds and increase in-job parallelism. Cost per unit of run time rises with runner size, but a shorter run consumes fewer minutes, so the billed amount can stay flat or even drop while waiting time falls.Automate releases
A manual release — writing the changelog and running deploy steps in the right order — is where mistakes and delays creep in. The whole sequence can be automated:1
Keep a traceable history
Release notes are assembled from the history of merged pull requests and
their commits. See
Commit messages for how to
write messages that read well later.
2
Generate the release notes automatically
Assemble the list of pull requests merged since the last release as the
release notes, so nobody hand-edits a changelog.
3
Reduce a deploy to merging a release pull request
One workflow generates a release pull request. A separate workflow,
triggered by the push to
main when it merges, runs the deploy. A
production release becomes a review and a merge instead of a runbook.develop collects merged work and main mirrors production — a
simplified form of git flow.
Triggered manually, it cuts a dated release branch from develop, assembles the list of
pull requests merged since the last release into the body, and opens a release
pull request into main:
The pull request list is extracted from merge commits, so this workflow
assumes pull requests are merged into
develop with “Create a merge
commit”. With squash merges, replace the extraction with a query such as
gh pr list --state merged.main — the merge of the release
pull request — and runs the production deploy:
concurrency prevents overlapping deploys when pushes land in quick
succession. The environment designation applies GitHub’s protection rules and
environment-scoped secrets to production.
The same approach removes recurring friction elsewhere in the workflow:
assigning the author to their own pull request, applying labels based on branch
name or changed paths, and extracting checked items from the pull request
template as a QA hand-off at release time.
Notify pipeline results
A pipeline shortens the feedback loop only if its results reach the people who act on them. Two channels cover most cases:- Status checks on the pull request. The CI service reports each job’s result on the pull request itself. This is built in and covers feedback while a change is under review.
- Chat notifications. Events that happen outside a pull request — a production deploy finishing, a scheduled workflow failing — need a push channel such as Slack.
Deployment frequency as a result, not a target
Deployment frequency is a widely used productivity metric, and a high value is the result of working practices: automated pipelines, small well-scoped changes, and a test suite that keeps the risk of each change low. Optimizing the number itself inverts the causality and degrades the outcome it is meant to measure:- Splitting unchanged work into more deploys adds operational overhead and raises the change failure rate without delivering more value.
- Lowering deploy frequency to improve the failure rate batches more changes into each deploy, which makes failures more likely and their causes harder to isolate.