DEV Community

Jan Duris
Jan Duris

Posted on

CI Is Green. What Could Still Go Wrong?


A green CI pipeline is a nice feeling.

The build passed.
Tests are green.
Linting is clean.
Nothing obvious is on fire.

For a few seconds, it feels like the release question has already been answered.

It hasn't.

A green pipeline tells you something useful: the checks you configured have passed under the conditions in which they were executed.

That is not the same thing as saying the release is safe.

And the gap between those two ideas is where a lot of production problems begin.

I touched on this in my previous article about why a green CI pipeline can create a false sense of security. But there is another question that deserves more attention:

If CI is green, what can still go wrong?

Quite a lot, actually.

CI only knows what you taught it to check

This is probably the most important limitation.

CI is not intelligent by default.

It does not understand your product, your users, your business risk, or the weird operational history of your system.

It runs instructions.

If those instructions cover the right risks, great.

If they don't, the pipeline can be perfectly green while the release is still dangerous.

This is not really a failure of CI.

It is a failure of interpretation.

We often treat CI as if it has answered a bigger question than it actually has.

The pipeline says:

"The checks I know about passed."

Humans often hear:

"This release is safe."

Those are very different statements.

The code can be correct and the release can still fail

Imagine a change that passes every unit test and every integration test.

The code itself is fine.

Then it reaches production and fails because of configuration.

Wrong environment variable.

Missing secret.

Different feature flag state.

A service points to the wrong endpoint.

A timeout is configured differently in production.

A permission exists in staging but not in production.

None of those problems necessarily mean your tests were bad.

They mean the release depended on conditions outside the scope of those tests.

This is one of the reasons I think we should stop treating release readiness as purely a testing problem.

Testing is part of it.

It is not the whole thing.

Database migrations are a good example

Database changes are one of those areas where "green" can be misleading.

The migration itself may be syntactically correct.

Tests may pass against a fresh database.

Everything can look completely normal.

But production has something your test environment often does not have:

history.

Real data.

Old records.

Unexpected null values.

Large tables.

Long-running transactions.

Different indexes.

Different traffic patterns.

A migration that takes two seconds in CI can behave very differently against a production dataset that has existed for six years.

The pipeline can be green because nothing it executed was technically wrong.

The release can still be risky because the environment changed the meaning of that change.

Dependencies can change the picture

Modern software rarely runs in isolation.

A release can depend on:

another internal service
a third-party API
a package update
a cloud service
a message queue
a payment provider
a database
an authentication system

Your own repository may be completely healthy.

The dependency chain around it may not be.

Maybe another service has not deployed the expected API yet.

Maybe a backwards-compatibility assumption is wrong.

Maybe one dependency is already degraded.

Maybe the release introduces a combination of versions that nobody has actually tested together.

Again, CI may remain perfectly green.

It tested what it could see.

The release depends on more than that.

Passing tests do not mean the right tests ran

This one is uncomfortable, because it sounds obvious once you say it.

A test suite can have a 100% pass rate while still telling you very little about the change you are about to release.

The interesting question is not:

How many tests passed?

It is:

Did we test the things that matter for this release?

Those are not equivalent.

If somebody changes a critical authentication flow and your pipeline runs 3,000 tests mostly covering catalog functionality, the number "3,000 passed" looks impressive.

But what does it actually tell you about authentication risk?

Not much.

This is why I find the concept of risk coverage more useful than simply looking at test coverage or test count.

What changed?

What can that change affect?

Which checks give us evidence about those areas?

That context matters more than a big green number.

Feature flags do not magically remove risk

Feature flags are useful.

Very useful.

But sometimes they become another source of false confidence.

The thinking becomes:

"It's behind a flag, so the release is safe."

Maybe.

But the code still shipped.

The flag configuration itself can be wrong.

The disabled path may affect initialization.

The new code may introduce dependencies before the flag is evaluated.

The wrong audience may receive the feature.

The rollback path may not behave the way you expect.

Feature flags reduce certain kinds of risk.

They do not eliminate release risk.

That distinction matters.

Rollback is not a guarantee either

Another sentence I hear in software teams is some version of:

"If something goes wrong, we'll just roll it back."

That can be a perfectly reasonable risk strategy.

But "rollback exists" and "rollback is safe" are not the same thing.

What happens if the release includes a destructive migration?

What happens if new data has already been written in a new format?

What happens if another service starts depending on the new behavior?

What happens if the release changes an external contract?

Rollback sounds simple when we imagine software as a single binary moving backwards.

Real systems are messier.

Sometimes the release creates state that cannot be cleanly reversed.

A green CI pipeline usually does not tell you whether your rollback strategy actually works.

Production traffic is different

There is also a class of failures that only appears under real usage.

Concurrency.

Traffic spikes.

Unusual request sequences.

Large payloads.

Rare account states.

Slow upstream services.

Network instability.

Real users are extremely good at discovering combinations that test environments never produce.

This is not an argument against automation.

It is an argument for understanding what automation can and cannot prove.

A test suite can give you evidence.

It cannot give you certainty.

Observability matters before the release, not only after it

One thing I think teams often underestimate is operational readiness.

Suppose the release does contain a problem.

Will you notice?

How quickly?

Do you have the right metrics?

Do you have logs around the changed area?

Will an alert actually fire?

Can you distinguish expected noise from a real regression?

A release with some uncertainty but excellent observability can sometimes be safer than a release with more passing tests but poor visibility.

Because release safety is not only about preventing every possible failure.

It is also about limiting the damage when a failure happens.

That changes the release decision.

Human context still matters

Not every release risk is technical.

Sometimes the biggest risk is simply that nobody has the full picture.

QA knows about one unresolved issue.

A developer knows the fix was rushed.

Operations knows a dependent system has been unstable all afternoon.

Product knows a feature absolutely must not reach one customer segment yet.

Security raised a concern that is technically "non-blocking."

Each piece of information exists.

But it exists in different places, owned by different people.

Then the pipeline turns green.

And everyone assumes somebody else must have checked the rest.

This is where release processes become dangerous.

Not because there was no information.

Because the information never became part of the decision.

So what does green CI actually mean?

I still want green CI.

Obviously.

A red pipeline is useful information too, but I would much rather start from green.

The problem is not the green status.

The problem is giving that status more authority than it deserves.

For me, a green pipeline should mean something closer to:

The automated checks we currently require did not detect a blocking problem.

That is a strong signal.

It is not yet a release decision.

A real release decision needs more context.

Depending on the system, I would want to know things like:

What changed?
How risky is that area?
Which relevant tests actually ran?
Are there unresolved defects?
Are dependencies healthy and compatible?
Are migrations involved?
Are security checks clean?
Do we have sufficient observability?
Can we roll back safely?
Is there any known operational or business risk?

Only then does the question become interesting:

Do we have enough evidence to release?

Maybe the problem is the way we frame CI

We built CI to automate verification.

Over time, many teams started using it as a proxy for release readiness.

That shortcut works surprisingly often.

Which is exactly why it is dangerous.

Most releases with a green pipeline are probably fine.

Then one isn't.

And when you investigate what happened, the answer is often not:

"CI failed."

It is:

"CI never knew that this was something it needed to evaluate."

That is the gap I keep coming back to.

The future of release engineering, in my opinion, is not just more tests and faster pipelines.

It is better decision-making.

More context.

Better evidence.

A clearer understanding of what we know before we put a change in front of users.

Green CI should absolutely make us more confident.

It just shouldn't make us stop asking questions.

Top comments (0)