Every software project has a treacherous moment: the one where someone says “it’s done” and it isn’t live yet. In between fit a forgotten review, a variable nobody set and a manual deploy on a Friday night.
The pipeline exists so that this gap doesn’t depend on anyone remembering anything.
The starting point
Let’s follow a fictional but very typical change: the client wants the contact form to accept phone numbers with a country code. The issue says what must be true at the end — the field accepts numbers from other countries and still rejects anything that isn’t a phone number.
An agent picks up the issue, works on an isolated copy of the repository and delivers a merge request to the integration branch. So far, nothing has been published. And nothing will be without going through the rest.
The gates, in order
Every merge request crosses the same sequence. The configuration looks something like this:
stages: [verify, review, publish, confirm]
types-and-tests:
stage: verify
script:
- bun install --frozen-lockfile
- bun run check
- bun test
secrets:
stage: verify
script:
- gitleaks detect --no-banner
publish:
stage: publish
rules:
- if: $CI_COMMIT_BRANCH == "staging"
script:
- ./deploy.sh
confirm:
stage: confirm
script:
- ./smoke.sh
Each stage has a different job, and none replaces another:
| Stage | What it guarantees |
|---|---|
| Verify | the code builds, tests pass and no secret leaked |
| Review | someone who didn’t write the code confirms it delivers the request |
| Publish | the approved version goes live without manual steps |
| Confirm | what is live really is the new version — and it responds |
The review isn’t done by the same agent
The agent that wrote the code does not approve its own work. The review is done by another agent, with other instructions, whose only goal is to find what isn’t right: half-delivered acceptance criteria, broken behavior, security flaws.
If the review fails, the work comes back with the reason written down. If it passes, the merge happens — and only then does publishing begin.
Published is not the same as live
This is the detail most pipelines ignore. A deploy command finishing without errors proves nothing. It may have shipped the old version again. It may have shipped the new one and it isn’t responding.
That is why the last stage checks the effect: the running service really was replaced, and the new version responds as it should. If not, the pipeline turns red and we find out before the client does.
A green pipeline doesn’t prove an effect. You prove the effect by looking at the effect.
How long does it take?
For a small change like the phone field, the whole pipeline takes a few minutes. The exact number matters less than the fact that it is always the same path: a one-line change and a thousand-line change cross the same gates.
That is what lets us deliver many times a day without any of them being a leap in the dark.