You already know what a CI/CD pipeline is for. This is the build sheet: the stages to wire up, the order to run them in, and the gates that stop a bad change before it reaches users. The walkthrough stays neutral on platforms, since the shape is the same whether you run GitHub Actions, GitLab CI, CircleCI, or Jenkins. For a refresher on the concepts behind each stage, see the CI/CD pipeline guide.
Pick a CI platform#
Start with a runner that lives close to where your code already is. GitHub Actions, GitLab CI, CircleCI, and Jenkins all do the same core job: watch the repo, run a config file, and report results back. The deciding factors are usually where your code is hosted, what your team already knows, and whether you need self-hosted runners.
Whatever you pick, the pipeline is defined as a file in the repo. That keeps it versioned alongside the code it ships, so a change to the pipeline goes through the same review as a change to the app.
Define the trigger#
Decide what makes the pipeline run. The two triggers worth wiring from day one are push and pull request.
- Pull request runs check a change before it merges, so a reviewer sees a green or red result next to the diff.
- Push runs on your main or release branch guard the exact line you deploy from.
Most teams run the full pipeline on both, and reserve the deploy stages for the branch they release from.
Add a build stage#
The build stage installs dependencies, compiles, and packages the app. Keep it deterministic: pin versions and use a clean environment so the same commit always produces the same output. If the build fails, the run stops here and nothing downstream wastes time. A clean build is the first gate.
Run the automated tests in order#
Tests are where the pipeline earns its keep, but order matters. Run them fastest first so failures surface quickly:
- Unit tests run first. They are the quickest and catch the most common breakage.
- A smoke test runs next as the fast gate. It confirms the app starts and its critical paths respond before you spend minutes on the heavier suites.
- Integration and end-to-end tests run last, only once the quick checks have passed.
This ordering means a typo or an obviously broken build fails in seconds, not after a twenty-minute end-to-end run.
Produce a deployable artifact#
Once tests pass, package the build output as a versioned artifact or container image and store it. Every later stage deploys that same artifact rather than rebuilding. This guarantees the bytes you ship to production are the exact bytes you tested, and it gives you a known-good version to roll back to.
Deploy to staging, then production#
Promote the artifact through environments instead of building separately for each. Deploy to staging first, run checks against it, then deploy the same artifact to production. For the production step, use a release pattern that limits how much traffic a bad version can reach. Rolling, blue-green, and canary deploys each trade speed for safety differently; the deployment strategies guide covers when each one fits.
Gate on red#
A pipeline is only as useful as its willingness to stop. Configure every stage to fail the build when a step fails, and block merges and deploys on a failing run. A red test that still lets a change through is worse than no test, because it teaches the team to ignore the signal. The gate is the whole point: a broken change should never reach the deploy stage.
Handle secrets and environment config#
Real pipelines need tokens, API keys, and database URLs. Keep them out of the repo:
- Store secrets in your platform's secret store and inject them as environment variables at run time.
- Keep per-environment values separate, so staging credentials never touch production.
- Scope each secret to the stages that actually need it.
Committing a secret to git is hard to undo and easy to avoid.
Verify after deploy#
Shipping is not the same as working. Add a post-deploy step that runs a smoke check against the live environment and confirms the release is up and serving real traffic. If that check fails, treat it like any other red gate: alert, and roll back to the previous artifact. This closes the loop between "deployed" and "actually working."
Gate order that works#
The reason to run a smoke test before the full suites is economics. A smoke test answers one question fast: is the build fundamentally alive? If it is not, there is no point running a slow integration or end-to-end suite against it. Putting the cheap, broad check ahead of the expensive, deep ones means most failures cost seconds instead of minutes, and your pipeline stays fast enough that people trust it. The smoke testing versus regression testing comparison explains how the two layers divide the work.
Common mistakes#
- No real gate. Tests run but failures do not block the deploy. The pipeline reports, but it does not protect.
- Tests in the wrong order. Running the slow suites first makes every failure expensive and trains people to skip the pipeline.
- Rebuilding per environment. Building separately for staging and production means you ship something you never tested. Build once, promote the artifact.
- Secrets in the repo. Convenient today, an incident later. Use the secret store from the start.
- No post-deploy check. A green pipeline tells you the artifact was good in CI, not that the live site is up. Verify after deploy.
Conclusion#
A working pipeline turns release into routine: every change builds, runs unit then smoke then the deeper suites, gets gated on red, and ships the same way every time. Set the trigger, order the tests, gate hard, promote one artifact, and verify after deploy, and you have the spine of a reliable delivery process.
A pipeline gets a good version into production. Confirming it stays working there is a separate job. ObserveOne runs scheduled checks against your live endpoints from multiple regions, so the same kind of verification that gates your deploy keeps watching after it, and you hear about a regression before your users do.