Your tests passed.
CI is green.
The build succeeded.
So you release.
Seems reasonable, right?
Not necessarily.
One of the most dangerous assumptions in modern software delivery is that a successful test suite automatically means a release is ready for production.
It doesn't.
Passing tests tell us that the checks we wrote passed.
A release decision asks a much larger question:
Do we have enough evidence to accept the risk of shipping this change?
Those are not the same thing.
What does a passing test actually prove?
Every automated test checks a specific expectation.
A unit test might verify that a function returns the expected result.
An API test might verify that an endpoint responds with 200.
An end-to-end test might confirm that a user can complete checkout.
All of these are useful.
But every test is created around assumptions.
Someone decided:
- what should be tested,
- which scenarios matter,
- which data should be used,
- which environments should be covered,
- and which failure modes were worth checking.
So when a test passes, what we really know is:
Under the conditions we tested, the expected behavior occurred.
That is valuable evidence.
But it is not the same as:
This release is safe.
A release contains more risk than your test suite can see
Imagine this release:
Unit tests PASS
Integration tests PASS
E2E tests PASS
Lint PASS
Build PASS
Everything looks perfect.
But the same release also includes:
Database migration HIGH IMPACT
Authentication changes YES
Dependency upgrade YES
Rollback recently tested NO
Observability coverage PARTIAL
Deployment blast radius HIGH
None of those conditions necessarily need to make a test fail.
Yet every one of them can influence whether releasing is a good idea.
This is where we often confuse two different activities:
verification and decision-making.
Tests verify behavior.
Release decisions evaluate risk.
Green CI is a signal
A common delivery model effectively looks like this:
Tests pass
+
Build succeeds
+
Artifact exists
=
Deploy
For small systems and simple changes, this can work remarkably well.
But the larger the system becomes, the weaker that assumption gets.
A release can have a perfectly healthy CI pipeline and still be operationally risky.
For example:
green pipeline
+
critical service change
+
large migration
+
weak rollback confidence
+
limited observability
???
The individual checks are green.
The combined risk might not be.
Tests should be evidence, not the final decision
I think a better mental model is this:
RELEASE DECISION
▲
│
┌───────────────┼───────────────┐
│ │ │
Tests Changes Operations
│ │ │
Security Dependencies Rollback
│ │ │
└──────────── Evidence ─────────┘
Tests remain one of the strongest signals we have.
But they are one signal among several.
A release decision may also need to consider:
change size,
affected services,
security findings,
dependency changes,
database migrations,
production incident history,
deployment timing,
rollback readiness,
observability,
required approvals.
Not every team needs every signal.
The important part is the distinction:
Test results are evidence presented to a release decision. They are not the release decision itself.
This gets more important with AI-generated code
AI coding tools are making software creation dramatically faster.
They can generate implementation code.
They can generate tests.
They can fix failing tests.
And they can do all of that very quickly.
That creates an interesting situation:
More code
More tests
More automation
More green checks
But potentially:
Less human understanding of every individual change
An AI-generated implementation can satisfy the tests while still introducing:
a new architectural assumption,
an unexpected dependency,
different runtime behavior,
operational complexity,
or a failure mode nobody encoded into the tests.
The solution is not to stop using AI.
And it isn't simply to generate even more tests.
The solution is to become better at evaluating evidence around the release.
CI systems are excellent at execution
Modern CI/CD systems are incredibly capable.
They can:
run thousands of tests,
scan dependencies,
build containers,
validate infrastructure,
generate artifacts,
and deploy applications.
But there is one question many pipelines are surprisingly bad at answering:
Why are we comfortable releasing this version?
Ask that question after a deployment.
You may get answers like:
"Everything was green."
or:
"The PR was approved."
or:
"The pipeline allowed it."
Those answers describe what happened.
They don't necessarily explain the actual release decision.
What would a release decision layer look like?
Instead of:
CI green → deploy
we could think about delivery like this:
Code change
↓
CI / QA / Security / Operational signals
↓
Evidence collection
↓
Risk evaluation
↓
Policy
↓
Release decision
↓
Deploy
That decision does not always need to be binary.
For example:
GO
The evidence is sufficient and the risk is acceptable.
CONDITIONAL GO
The release can proceed, but only with an approval, mitigation, feature flag, additional monitoring, or another condition.
NO GO
The available evidence or risk does not justify releasing yet.
This is much closer to how experienced engineers already think.
The difference is making that reasoning explicit instead of leaving it inside someone's head.
Green should have a precise meaning
There is nothing wrong with green CI.
We want green builds.
We want passing tests.
We want fast feedback.
The problem begins when we give those signals more meaning than they actually contain.
A green pipeline should mean:
The checks we defined passed.
A release decision should mean:
Given the available evidence and our risk policy, we are willing to ship this change.
Those statements are related.
But they are not identical.
And as software development becomes faster, more distributed, and increasingly AI-assisted, I think that distinction is going to matter much more.
How does your team decide that something is ready to release?
Is a green pipeline enough?
Or is there another decision layer after CI?
Top comments (0)