Flaky Test Quarantine: Owners, Expiry, and Nonblocking CI Results
A confirmed flaky test can block unrelated changes without providing a dependable release signal. Removing that obstruction is reasonable only if someone still sees…
A combined test report can look healthy while a missing CI job has contributed no results. Adding machines makes this easier to overlook: the visible reports describe the work that arrived, not necessarily the work the pipeline intended to run. A faster result is useful only when engineers can explain which tests ran, which did not, and where the original failures can be inspected.
Parallel test execution runs independent work at the same time. Test sharding divides a selected suite into partitions that CI jobs execute separately; each job may also run several local test workers. Scale those layers deliberately, keeping the selected suite unchanged and making partition completeness a required check alongside test outcomes.
Start with a reproducible definition of the run. All shards need the same source revision, dependency lockfile, test configuration, selection filters, projects, and intended environment. If one job discovers a different inventory because an environment variable changes selection, a correct partitioning algorithm cannot repair the mismatch.
Record a run manifest before distributing work. This is a recommended control to implement in your pipeline, not a promise that every framework generates it automatically. Include:
Choose a logical execution key that distinguishes real planned work. A stable framework case identifier can be a starting point; otherwise use a validated combination of repository-relative file, full test title, and parameter identity. Include project or browser configuration, deliberate repeat index, and environment when those represent separate executions. A title alone may identify several different cases.
Keep that key separate from the observation key, which adds workflow attempt, shard, and retry attempt. The same case legitimately produces several retry observations. Conversely, including the shard number in the logical key would conceal an accidental assignment of the same case to two shards. Track setup or dependency records separately so expected repeated setup does not look like duplicated business-test coverage.
Check the plan before execution: its assignments should cover the selected runnable inventory without accidental overlap. Then check actual results against that plan. Equal totals are insufficient because a duplicate case can replace a missing one without changing the count.
A shard count describes partitioning, not guaranteed simultaneous execution. The CI scheduler decides which jobs can start, while the test runner controls workers inside each job. GitHub Actions, for example, uses max-parallel to limit concurrent matrix jobs. Playwright's workers setting limits local test-worker processes. These controls govern different resources.
Consider an illustrative configuration with four shards, at most three simultaneous matrix jobs, and two test workers per job. Its configured ceiling is six concurrent test-worker slots. Actual concurrency may be lower because runner capacity, scheduling, setup, or the amount of runnable work limits utilization. This calculation predicts neither browser throughput nor pipeline speedup.
Set the budget against the application environment as well as the CI machines. More jobs may increase database connections, authentication requests, and pressure on shared services even when each runner has spare CPU. Excess local workers can also compete for memory and CPU within one runner. A slow or unstable environment is a reason to investigate capacity, not automatically to add shards.
During the first comparison, hold the worker limit constant and vary the shard count. Record job queue time and resource contention so a scheduler bottleneck is not mistaken for poor test distribution. If jobs must wait in waves, evaluate the actual timeline rather than assuming every partition starts together.
The smallest safely distributable unit determines how much balancing is possible. A scheduler cannot divide a dependent sequence into independent cases merely because another machine is idle. Establish those boundaries before deciding whether to change file organization, runner configuration, or allocation logic.
Current Playwright documentation distinguishes file-level sharding from individual-test sharding. Without fullyParallel, whole files are the normal distribution unit; with fullyParallel enabled, individual tests can be distributed across shards. This can reduce imbalance caused by files with very different test counts, but balancing counts does not balance execution duration.
Enabling individual-test parallelism also changes lifecycle assumptions. Playwright runs parallel tests in separate workers, and relevant hooks can execute for each test. A fixture that modifies shared records, assumes a previous case ran first, or performs expensive repeated initialization needs review before changing that mode.
Keep intentionally dependent sequences together until their dependencies are removed. In Playwright serial groups, a failure can skip later tests and the group is retried together. Preserve those outcomes rather than reporting every assigned case as executed. A long indivisible group can remain the limiting shard even after shorter work is redistributed.
Collect durations at the same granularity you can assign: file, class, test, or approved group. Compare runs with compatible runner resources, environment, and configuration. Stale timings from a different browser or machine can produce a precise-looking but poor allocation.
Where the selected tooling supports historical-duration splitting, use it with matching test identities and timing fields. CircleCI documents this approach, although its available commands and recommendations differ between Cloud and Server. Do not assume that a framework's ordinary count-based sharding option also uses historical duration.
For a custom static plan, a useful heuristic assigns longer estimated units first to the least-loaded eligible partition. Keep required groups intact, define a fallback estimate for new tests, and save the resulting plan so every shard consumes the same assignments. Treat this as an estimate to validate, not an optimal schedule or a replacement for the framework's supported selection mechanism.
Measure the entire feedback path: queueing, setup, test execution, artifact upload, and final aggregation. Repeated setup can consume much of the benefit of finer partitions. Retries can create a long tail on a previously balanced shard. Compare the slowest required completion and total runner use, not only average test duration or one unusually fast run.
Separate browser contexts do not create separate backend accounts or database records. In an illustrative refund suite, one case changes a finance user's permissions while another checks that user's refund access. Isolated browsers still observe the same server-side change. Give concurrent mutable tests independent accounts or reserved records, or enforce a supported isolation boundary around their work.
Choose names according to resource lifetime. Persistent resources may need the workflow run, run attempt, shard, project or environment, and local worker identity; test-created entities may also need the logical case and retry attempt. A worker index that is unique inside one runner is not a globally unique reservation across several jobs or overlapping workflows.
A database per job, a schema per worker, or namespaced entities can all be reasonable designs if the application respects those boundaries. Avoid cleanup that truncates shared tables or deletes another shard's records. Register scoped teardown and expiry-based cleanup for abandoned jobs. For provisioning and reset details, use the test data management guide.
Apply the same discipline to screenshots, traces, exported files, and reports. Test-scoped output paths prevent local collisions; uploaded artifact names must additionally distinguish the relevant shard, project, and run attempt. Preserve enough metadata to reconnect an attachment to its result after download.
Keep authentication state out of broadly shared diagnostic bundles. Playwright warns that saved browser state can contain credentials capable of impersonating an account. Restrict access and retention, redact sensitive logs, and retain only the evidence an investigation needs. A useful failure report should not become a credential distribution mechanism.
Design artifact collection to survive an assertion failure without converting that failure into success. The official Playwright sharding example disables matrix fail-fast and runs report upload and aggregation under a not-cancelled condition. That supports diagnosis after failed tests; it does not guarantee files survive a runner crash or deliberate workflow cancellation.
For Playwright's blob-report workflow, use this sequence with the framework version pinned by your project:
--shard=1/4 for the first partition of a four-shard plan.--reporter=html ./all-blob-reports to produce a human-readable report.Blob reports are Playwright's merge input, not a universal interchange format for arbitrary test outputs. JUnit XML is a separate reporter format; use an aggregator that understands the format your framework produces. Preserve project and environment identity when combining repeated execution across configurations. Playwright also documents extra merge configuration when inputs come from different operating systems.
Playwright's blob report includes test results and collected test attachments, including traces and screenshot differences. It cannot recover a trace that was never captured, an unflushed report, or an external service log that was never attached. Verify the evidence policy before relying on the merged view. Retain raw shard reports and needed redacted logs so rendering or download problems do not erase the original observation.
An upload condition is only part of that policy. If an older run is cancelled, record that it is incomplete. If a runner disappears before writing its report, retain the available CI logs and classify the infrastructure loss. Merging the remaining reports can still help investigation, but cannot establish that the absent work passed.
Require the summary to reconcile planned partitions, observed execution identities, and outcomes independently. Successful report generation establishes that available inputs were rendered; it does not establish full suite execution.
The following is an illustrative four-shard run, not a recorded CI result:
Planned Partition | Received Evidence | Summary Interpretation |
|---|---|---|
Shard 1/4 | Report received; assigned cases passed | Accept this partition's evidence after identity checks |
Shard 2/4 | Report received; assigned cases passed | Accept this partition's evidence after identity checks |
Shard 3/4 | No report; job cancelled or lost | Mark incomplete; do not infer a pass |
Shard 4/4 | Report received; assigned cases passed | Accept this partition's evidence after identity checks |
Three received reports leave one planned partition unaccounted for. Even if every received result passes, the run must not satisfy a gate that requires complete execution. A missing partition and a missing test identity are separate checks: a report can exist while containing only part of its assigned work.
Compare actual units with the canonical inventory, allowing only explicitly defined multiplicity for projects, repeats, and retries. Flag missing units, unexpected units, and accidental duplicates. Check commit and configuration identity before combining records; a complete report from an older attempt cannot silently fill a gap in the current run.
Keep outcome classification explicit. A skipped case, a setup-blocked case, and a test that never started are not executed passes. Playwright distinguishes cases that pass immediately from cases that fail and later pass on retry. Preserve the first failure and retry evidence even when policy permits the final result. Quarantine requires a recorded scope decision and owner; removing a troublesome case is not proof that sharding preserved the original suite.
Completeness and success are separate decisions. A complete run may fail assertions. An incomplete run may contain no observed failures. Configure the required summary check to reject missing evidence and propagate disallowed outcomes, rather than returning green whenever report generation succeeds.
Give the investigating engineer the commit, inventory and assignment manifest, project selection, shard fraction, worker limit, run attempt, relevant environment reference, and access-controlled artifacts. Reproducing a partition requires its selection context as well as its command. Secrets should come from an approved developer setup, not copied CI authentication files.
Assign the workflow owner to missing jobs and artifact collection, the test owner to assertions and fixture dependencies, and the environment owner to contention or shared-state collisions. Preserve the original record before rerunning. A successful rerun is another observation, not a reason to overwrite the failed attempt.
Before making the sharded pipeline the sole required route, compare it with the existing workflow using the same inventory and controlled build and environment. Demonstrate that the completeness check detects an omitted partition and that a failed test still leaves inspectable evidence. These are rollout checks for your implementation, not results established by this guide.
Choose the smallest measured configuration that meets the feedback target without losing execution identity or diagnostic evidence. Increase concurrency only after the team can distinguish a slower shard from a missing one and a complete passing suite from a partial report.
A confirmed flaky test can block unrelated changes without providing a dependable release signal. Removing that obstruction is reasonable only if someone still sees…
A tester reviewing a Jira issue may need a fresh smoke run without opening GitLab and choosing pipeline parameters. A manually triggered Jira Cloud…
A regression-results worker receives HTTP 429 while reading Jira issues. Sleeping that worker may be necessary, but it will not help if other workers…
Create traceable test cases, prepare release-specific test runs, and keep approved coverage current as the product changes.