June 11, 2026
How to Set a Flaky Test Exit Policy That Reduces Noise Without Hiding Release Risk
Learn how to design a flaky test exit policy for reruns, quarantines, fail-fast behavior, and escalation, so you improve build health without hiding release risk.
Flaky tests are not just a nuisance, they are a decision problem. Every time a pipeline turns red, someone has to decide whether the failure is real, whether to rerun, whether to quarantine, and whether to trust the release. Without a clear flaky test exit policy, teams slowly build a habit of treating unstable test results as background noise. That habit is expensive, because it lowers build health, weakens CI signal quality, and can hide genuine release risk behind a pile of exceptions.
A good policy does not pretend flakes can be eliminated overnight. It creates a controlled path for handling them, so the team can keep shipping while making the system more trustworthy over time. The goal is not to make every build green. The goal is to make every failure meaningful.
What a flaky test exit policy actually decides
A flaky test exit policy is the set of rules that determines what happens after a test fails intermittently. It should answer four questions:
- Do we rerun the test, and if so, how many times?
- Do we quarantine the test from release gating?
- Do we fail the pipeline immediately, or only after confirmation?
- Do we escalate the issue to an owner, and with what severity?
If those choices are left implicit, teams end up with inconsistent behavior across pipelines, environments, and people. One engineer reruns twice. Another merges anyway. A third creates a ticket nobody reads. The result is not flexibility, it is noise.
A flaky test policy is not a workaround for poor tests, it is a control system for preventing bad signals from becoming normal.
To design the policy well, separate two concerns:
- Signal quality, whether the test result can be trusted right now.
- Release risk, whether ignoring the failure could let a real defect escape.
The mistake most teams make is optimizing only for signal quality in the short term, usually by rerunning until green. That can make the dashboard look better while masking the fact that the build is no longer a reliable release gate.
Start by classifying failure types
Before you define actions, define categories. Not every intermittent failure deserves the same response.
1. Product defects that appear unstable
Sometimes the application really is broken, but the symptoms are timing-sensitive or environment-sensitive. For example, an API call may race with background processing, or a UI state may depend on an eventually consistent backend. These are not flaky tests in the strict sense, they are legitimate failures that need a more robust test or product fix.
2. Test defects
These are failures caused by the test itself, unstable locators, poor synchronization, data collisions, shared state, dependency on execution order, or brittle assertions. These should usually be treated as engineering debt, not release blockers, unless they are hiding a dangerous gap in coverage.
3. Environment defects
Infrastructure problems can look like flaky tests, including network timeouts, test runner contention, database resets, feature flag drift, or third-party outages. A policy should distinguish these because the remediation path is often operational rather than code-level.
4. Unknown intermittent failures
These are the hardest. The failure is real, but the cause is unclear. That uncertainty is exactly why the exit policy matters, because it defines what happens while investigation is pending.
A simple classification matrix makes the policy easier to apply consistently:
| Failure pattern | Likely category | Default action |
|---|---|---|
| Fails once, passes on rerun, same step, same data | Test or environment flake | Rerun once, capture telemetry, track recurrence |
| Fails repeatedly in same area | Probable product defect or systemic test defect | Fail fast, escalate |
| Only fails in one browser, region, or runner image | Environment or compatibility issue | Quarantine if low risk, escalate to platform owner |
| Passes on developer machine but fails in CI | Environment or synchronization issue | Investigate CI-specific assumptions |
The core policy decision tree
A practical flaky test exit policy should be simple enough to use under pressure. Here is a decision tree that works for many teams.
Step 1: Is the test release-critical?
If the test covers checkout, auth, payment, data loss, deployment safety, or another high-impact flow, treat it differently from a low-value non-blocking check.
- Release-critical test: do not hide failure behind unlimited reruns.
- Non-critical test: rerun can be acceptable, but with limits and visibility.
Step 2: Did the failure repeat?
If the test fails once and then passes on one controlled rerun, that is a signal, not a conclusion. Capture the first failure, rerun once, then record the result.
A reasonable default:
- 1st failure, rerun once automatically.
- If it passes, mark as flaky and keep the result visible.
- If it fails again, treat as a real failure or a systemic issue.
Do not allow endless reruns in the main pipeline. Unlimited retries create a green illusion and destroy trust in CI signal quality.
Step 3: Is the test already quarantined?
Quarantine is a containment measure, not a permanent home. A quarantined test should be excluded from the release gate, but still tracked in a visible queue with an owner and expiry date.
If the test is quarantined:
- It should not block the pipeline.
- It should still run in a non-gating job if feasible.
- It should generate a ticket or alert if it fails again.
- It should have a clear removal criterion.
Step 4: Does this failure increase release risk?
This is the part many teams skip. Not every flaky test has equal business impact. A flaky smoke test on a low-traffic admin page may be annoying. A flaky test around user signup or payments is different.
Ask:
- Does the test cover a critical user journey?
- Does it validate a rollback or deploy safety mechanism?
- Is the test the only automated check for this behavior?
- Would a missed defect be costly or security-relevant?
If the answer to any of those is yes, prefer fail-fast or at least fail-decision escalation, not silent reruns.
Recommended policy patterns by test tier
A good exit policy is usually tiered. One policy for everything is too blunt.
Tier 1: Release gate tests
These are tests that can stop a deployment. Examples include smoke tests, critical API health checks, authentication, checkout, and production readiness probes.
Recommended policy:
- Retry once only if the failure is clearly transient, such as a timeout.
- If the rerun passes, surface the result as a flaky failure, not a clean pass.
- If the rerun fails, fail the release gate.
- Require an explicit owner review before reclassifying the test as quarantined.
This protects build health without normalizing risk.
Tier 2: Merge validation tests
These are tests that protect main branch quality, but are not as immediately critical as release gates.
Recommended policy:
- Retry once automatically.
- If the rerun passes, mark unstable result and track it.
- If the same test flakes more than a defined threshold in a time window, quarantine it or remove it from the merge gate until fixed.
This tier benefits from a test quarantine policy, but the quarantine must be bounded and visible.
Tier 3: Informational or exploratory checks
These tests provide useful signal but should not stop delivery.
Recommended policy:
- Do not rerun by default unless failure triage depends on it.
- Route failures to a backlog or issue tracker.
- Use them as diagnostic signal, not as release gates.
If a team finds these tests failing constantly, either the test needs improvement or the test should not be running in the critical path at all.
What quarantine should and should not mean
Quarantine is often misused as a landfill for painful tests. That creates hidden risk. A real quarantine policy needs rules.
A quarantine policy should include
- Entry criteria: how flaky must a test be before quarantine?
- Owner: who fixes or reviews it?
- Expiration date: when does it get re-evaluated?
- Visibility: where is the quarantine list published?
- Exit criteria: what evidence is required to restore it to the gate?
Quarantine should not mean
- ignore forever,
- remove from metrics,
- hide from leadership,
- or let failing critical tests drift without accountability.
Quarantining a test is a temporary risk decision, not a proof that the risk no longer matters.
A good target is to make quarantines painful enough that people fix the root cause, but not so painful that they bypass the policy entirely.
Define rerun rules carefully
Reruns are useful when used as evidence, harmful when used as a crutch. If a failure disappears on rerun, that may tell you something. It does not make the original failure irrelevant.
A solid rerun policy should specify:
- maximum reruns per test,
- whether reruns are automatic or manual,
- whether reruns happen in the same environment,
- whether rerun outcomes are visible in the report,
- and whether rerun success changes gate status.
A practical default is:
- One automatic rerun for suspected transient issues,
- No hidden multiple retries for release-critical tests,
- Original failure always retained in reporting.
For CI systems, you can model this logic in the pipeline rather than inside the test. For example, a GitHub Actions job can rerun a test job once, but keep the first failure artifact and annotate the outcome.
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
continue-on-error: true
rerun-on-failure: needs: test if: needs.test.result == ‘failure’ runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm test
This is not the only implementation pattern, but it shows the key idea: reruns should be controlled, visible, and policy-driven.
Use thresholds, but do not worship them
Many teams want a simple rule such as “quarantine if it fails 3 times in 10 runs.” Thresholds are useful, but they should support judgment, not replace it.
Consider thresholds for:
- failure frequency over a time window,
- number of distinct branches affected,
- number of environments involved,
- and whether the test is blocking a critical path.
For example, a test that flakes once a week in a non-critical UI flow may be acceptable with tracking. The same flake in a critical deploy validation suite may be unacceptable after one recurrence.
A policy based only on failure counts can be misleading because it ignores business impact. A policy based only on judgment becomes inconsistent. Use both.
Make the ownership model explicit
A flaky test exit policy fails when nobody owns the decision. Every path should have an owner.
Suggested ownership model:
- Test owner: fixes the test or improves its reliability.
- Product owner or service owner: fixes genuine application defects.
- Platform or infra owner: fixes environment instability.
- Quality owner or release manager: enforces policy and approves exceptions.
For a large organization, separate operational control from local maintenance. The person running the pipeline should not be the only person responsible for deciding whether a flaky test can be ignored.
Instrument the pipeline so the policy is enforceable
A policy that lives in a wiki will drift. A policy that is encoded in the CI system is much harder to ignore.
At minimum, capture:
- first failure timestamp,
- rerun count,
- test identifier and suite,
- environment, browser, or runner image,
- branch and commit hash,
- owner or team tag,
- quarantine status,
- and age of the issue.
If you use test reports, make sure the original failure is visible, even when a rerun passes. Hiding the original failure is the fastest way to degrade CI signal quality.
A simple reporting rule helps:
A flaky test can be non-blocking, but it should never be invisible.
How to tie the policy to release risk
The best way to keep a flaky test policy honest is to connect it to release decision-making.
Questions that should influence the exit path
- Is the failing test in a business-critical path?
- Is the test the only automated check for this behavior?
- Is the failure correlated with deploy timing or data changes?
- Does the failure indicate a condition that production users could hit?
- Is the environment used for testing similar enough to production to make the signal meaningful?
If a flaky test covers a low-risk area, a rerun and temporary quarantine may be acceptable. If it covers a high-risk area, the safer choice is usually to fail the build and treat the result as a release blocker until proven otherwise.
Practical policy template you can adapt
Here is a concise policy structure many teams can start from:
- Run once normally.
- If failure is transient-looking, rerun once automatically.
- If rerun passes:
- keep the original failure visible,
- flag the test as flaky,
- assign an owner,
- open a ticket if the test is in a critical suite.
- If rerun fails:
- fail the pipeline,
- escalate as a likely real defect or systemic issue.
- If the test flaked more than the agreed threshold in a time window:
- quarantine it temporarily,
- review within a fixed SLA,
- define a removal deadline.
- If the test is release-critical:
- do not allow repeated retries to mask risk,
- require explicit human approval for exceptions.
This template is intentionally conservative. It favors transparency over convenience, which is usually the right choice when release risk matters.
Common mistakes that make flaky policies fail
Treating reruns as proof
A pass on rerun only proves the failure is intermittent. It does not prove the system is healthy.
Quarantining without expiry
Permanent quarantine is just hidden debt. It undermines trust in the test suite.
Mixing critical and non-critical tests in one policy
If everything is gated the same way, either the pipeline is too fragile or the policy becomes too lenient.
Ignoring environment instability
Sometimes the test is innocent. If the same suite fails across multiple unrelated tests, fix the runner, dependencies, data setup, or network path before blaming the tests.
Optimizing for green dashboards
A green pipeline with invisible reruns and untracked quarantines is not healthy. It is just quiet.
A workable operating model for managers and leaders
For QA managers, release engineers, engineering directors, and CTOs, the practical question is not whether flakes exist. They do. The question is whether the organization has a way to keep them from eroding release confidence.
A mature operating model usually includes:
- a written flaky test exit policy,
- an owned quarantine list,
- reporting on flaky trends by team and suite,
- SLAs for triage and removal,
- and release criteria that distinguish between temporary noise and genuine risk.
The management signal to watch is not just the number of flaky tests. It is how often the team has to make exceptions, how long those exceptions stay open, and whether the same failure patterns keep returning.
Final checklist for policy rollout
Before you adopt or update a flaky test exit policy, verify that you can answer these questions clearly:
- Which suites are release gates?
- How many reruns are allowed, and for which test tiers?
- What is the quarantine entry threshold?
- Who owns the fix for each class of failure?
- How are quarantined tests tracked and reviewed?
- What data is preserved when a rerun passes?
- When does a flaky result become a release blocker?
If the answers are vague, the policy will drift into exception-based operations, where the loudest failure gets attention and the quiet risk accumulates.
A strong flaky test exit policy does not eliminate uncertainty. It makes uncertainty manageable. That is the difference between a CI system that merely looks busy and one that genuinely supports safe delivery.
Related background
For readers who want a broader context on the terms used here, it can help to revisit the fundamentals of software testing, test automation, and continuous integration. The policy decisions in this guide sit at the intersection of all three.