BXUP Track progress & test yourself — free
Software Engineering · Playbook · Core · ~4 min read
SOFTWARE ENGINEERING · PLAYBOOK · CORE

Flaky-Test Forensics: Classify the Flake, Fix the Cause, Prove It

A flaky test is a debugging problem wearing a testing costume, and teams handle it with the two worst tools available: retry-until-green and @skip.

Tier: Core · Workflow area: Testing & Quality

AI changes the economics because flake diagnosis is fundamentally a correlation task — test source, CI history, timing patterns, and ordering effects considered together — and that is exactly what a model with all four in context does well. The discipline this playbook adds: classify before fixing, and prove determinism after.

When to use

  • A test fails ~1–20% of runs with no related code change.
  • CI retries are masking failures and the suite's signal is eroding ("just re-run it" is team vocabulary).
  • A test fails only in CI, only in parallel mode, or only after certain other tests.

Do not use for a test that fails 100% of the time under specific conditions — that is a plain bug; debug it directly (see the debugging playbooks in batch 1).

Prerequisites

  • CI history for the test: pass/fail record across ~50+ runs, with timestamps, runner, parallel shard, and durations if available (GitHub Actions API, Jenkins JSON, or your CI's flaky-test report).
  • The test source plus the fixtures/setup it uses.
  • Ability to run the suite locally with controlled ordering and repetition (pytest -p randomly, pytest-repeat, jest --runInBand).

The workflow

  1. Assemble the evidence bundle. One file: the test + its fixtures, 5–10 failure logs with timestamps, the pass/fail history, and notes on where it fails (shard? time of day? parallel only?). Diagnosis quality is bundle quality.

  2. Demand classification before any fix. Prompt:

    This test fails ~8% of CI runs. Evidence bundle below (source, fixtures,
    10 failure logs, run history with shard + duration).
    Classify the flake into: (a) timing/async race, (b) test-order dependency /
    shared state, (c) isolation failure (DB/files/ports not cleaned), (d) infra
    (runner resources, network), (e) concurrency in the code under test,
    (f) time/UTC/clock dependence.
    Rank the top 2 hypotheses WITH the specific evidence lines supporting each,
    state what evidence would distinguish them, and give a local reproduction
    command for the leading hypothesis. Do not propose a fix yet.
    
  3. Reproduce deterministically. Run the discriminating experiment: order dependency → pytest -p randomly --randomly-seed=<failing seed> or run the suspected pair alone; race → add load (stress-ng) or run pytest-repeat -n 200; clock → freeze time near midnight/DST. No reproduction, no fix — you would be patching a hypothesis.

  4. Fix the cause, not the symptom. Now ask for the repair, constrained: Hypothesis (b) confirmed: test_export reuses the module-scoped tmp dir mutated by test_import. Propose a fix that removes the shared state. Forbidden: sleeps, retries, skip markers, widening timeouts. Typical honest fixes: function-scoped fixtures, unique temp dirs/ports per test, awaiting the actual condition instead of sleeping, injecting a clock.

  5. Prove determinism. Re-run the reproduction command that previously failed — 200 repeats, the failing seed, the parallel mode — and record the green streak in the PR: "Failed 11/200 before, 0/500 after, seed 1847233."

  6. Institutionalize. Have the AI draft a one-paragraph entry for your team's flake ledger: symptom, class, root cause, fix pattern. Ten entries later, that ledger is context you paste into the next forensics prompt, and diagnosis gets faster every quarter.

Key Takeaway:

Retries convert a flaky test into a slow, dishonest one. Classification-first forensics converts it into a fixed one — and the fix pattern compounds across the suite.

Tool variants

  • Chat assistant (Claude/ChatGPT): best for the classification step — long evidence bundles reward large context windows.
  • Claude Code / Copilot agent mode: can run the repeat/reseed loop locally, bisect test orderings, and iterate on the fix; keep the forbidden-fixes list in the prompt or it will reach for a sleep.
  • CI-native flake detection (BuildPulse, Datadog CI Visibility, GitHub flaky-test features): use as the detector feeding this workflow, not as the fixer.

Verification

  • The flake reproduces on demand before the fix (seed, pair, or load recipe recorded).
  • Post-fix: 0 failures across a run count at least 10x the previous failure interval, in the same mode that flaked.
  • The fix contains no sleeps, retries, or skips; grep the diff for sleep|retry|skip and make the AI justify any hit.
  • Test duration did not balloon (a 30s "fixed" test is a new problem).

Pitfalls

  • Accepting the first plausible story. Models produce confident race-condition narratives that the evidence does not support — that is why the prompt demands evidence lines per hypothesis and a discriminating experiment.
  • Fixing in the test what is broken in the code. Class (e) flakes are production concurrency bugs surfacing in CI. Widening the test's tolerance ships the race to customers.
  • Bundling only one failure log. One log shows a symptom; ten logs show a pattern (always shard 3, always 02:00 UTC). Patterns drive classification.
  • Deleting the test under deadline pressure. A flaky test on a critical path is telling you something true; record a skip with an owner and a date if you must, never a silent delete.

Restricted-environment variant

CI logs routinely leak hostnames, internal URLs, tokens, and customer identifiers in stack traces. Scrub before pasting: strip env dumps, mask hostnames/IPs, redact bearer tokens (CI secrets masking is not sufficient for copy-paste). On client engagements route the bundle through the sanctioned tenant (Copilot Business / Azure OpenAI in-tenancy). Fully offline: the classification taxonomy in step 2 works as a human checklist, and a local Qwen-Coder-class model can still do log-pattern triage across your scrubbed bundle — weaker at multi-source correlation, so lean harder on the discriminating experiments.

Success criteria

  • Flake classified with evidence, reproduced deterministically, fixed without sleeps/retries/skips, and proven over a 10x run streak.
  • CI retry count for the affected job drops measurably.
  • A flake-ledger entry exists and has been reused in a later diagnosis.
  • Team default shifted from "re-run it" to "bundle it" — the cultural win that outlasts any single fix.
This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in