CI/CD Automation Tutorial 0/46 lessons ~6 min read Lesson 14

    Build Failure Triage

    Build failure triage is the operational discipline of assigning ownership, quarantining flaky tests, and bisecting regressions — so red CI means actionable signal, not backgroun…

    Course progress0%
    Focus
    20 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Build failure triage is the operational discipline of assigning ownership, quarantining flaky tests, and bisecting regressions — so red CI means actionable signal, not background noise engineers ignore.

    The story

    CI was red 40% of the time; teams clicked "Re-run failed jobs" until green. A real regression — bad SQL migration — hid among flakes for three days. Platform instituted triage: CODEOWNERS auto-assign on failure, flaky test quarantine file with 7-day SLA, and git bisect runbook tied to merge-base. MTTR for real failures dropped from 72h to 4h; flake rate tracked weekly dropped from 12% to 2% after quarantine enforcement.

    Understanding the topic

    Triage turns CI from alarm fatigue into routing + root cause — every red build gets an owner, classification (flake vs real), and resolution path.

    • Ownership: CODEOWNERS + failed job path → Slack/PagerDuty route; no "shared CI" orphan failures.
    • Flake quarantine: mark test @flaky or move to quarantine suite; must not block merge; tracked with expiry and fix SLA.
    • Git bisect: binary search between last green and red SHA to find introducing commit — automate with git bisect run + test script.
    • Classification: infra (runner OOM), dependency (registry down), test flake, product regression — different runbooks.
    • Metrics: flake rate, time-to-triage, re-run count per PR — DORA change failure rate input.

    Internal architecture

    Failure triage flow from red build to resolved root cause:

    text
    CI job fails
    Auto-notify CODEOWNERS (path from failed job/working dir)
    Classifier bot / on-call
    ├─ Re-run once → passes? → open flake ticket → quarantine
    ├─ Infra error (OOM, timeout) → platform ticket
    └─ Consistent fail → bisect
    git bisect start
    git bisect bad HEAD
    git bisect good <last-green-sha>
    git bisect run ./scripts/ci-check.sh
    Fix commit or quarantine with owner + expiry
    Post-mortem if prod-impacting; update flake registry

    Visual explanation

    Two diagrams show where Build Failure Triage lives in the delivery path and how teams implement it in production.

    Build Failure Triage — system view
    CI failure
    Red job
    Route owner
    CODEOWNERS
    Classify
    Flake vs real
    Resolve
    Fix/bisect
    Where this topic sits in the delivery path.
    Build Failure Triage — execution flow
    Quarantine
    Non-blocking
    Bisect
    Find commit
    Track SLA
    7-day fix
    Metrics
    Flake rate
    Follow this loop when designing or reviewing pipelines.

    Step-by-step explanation

    1. Enable CODEOWNERS on repo — map directories to teams; require review on failure paths.
    2. Add CI failure Slack notification with workflow run URL, failed job, and git SHA.
    3. Create quarantine mechanism: Jest @flaky tag, pytest mark, or separate non-required workflow for known flakes.
    4. Document bisect runbook: git bisect run script wrapping minimal repro command.
    5. Dashboard: flake rate = flaky re-runs / total runs; target <3%; weekly review of quarantine list.

    Production implementation

    Automated bisect in CI on main breakage + flake quarantine in Jest:

    • GitHub Actions: failed job sends to Slack via workflow run webhook + ownership from paths-filter.
    • Buildkite/analytics plugins track flaky test history automatically.
    • Require flake fix ticket link when adding to quarantine — bot enforces in PR.
    bash
    # scripts/ci-check.sh — exit 0 = good, 1 = bad (for bisect)
    #!/bin/bash
    set -e
    npm ci --prefer-offline
    npm test -- --testPathPattern="integration/auth"
    # On main red, run locally or in workflow_dispatch:
    git bisect start
    git bisect bad origin/main
    git bisect good v2.3.0 # last known green tag
    git bisect run ./scripts/ci-check.sh
    # git bisect reset when done
    # jest.config.js — quarantined tests (non-default project)
    module.exports = {
    projects: [
    { displayName: 'required', testPathIgnorePatterns: ['/quarantine/'] },
    { displayName: 'quarantine', testMatch: ['**/quarantine/**/*.test.ts'],
    runner: 'jest-runner' /* runs but not in required CI job */ },
    ],
    };
    # .github/CODEOWNERS
    /apps/api/ @team-payments
    /libs/auth/ @team-identity
    /.github/workflows/ @team-platform

    Execution workflow

    1Triage a CI failure end-to-end
    1 / 5

    Acknowledge

    Owner assigned via CODEOWNERS within SLA.

    Main red = 30 min; PR red = best effort.

    Real-world use

    Martin Fowler's "Eradicating Non-Determinism in Tests" and Google Testing Blog on flaky tests established quarantine patterns. git bisect is built into git since 2005 — underused because teams lack a one-command repro script. GitHub's merge queue increased triage urgency because main red blocks everyone.

    Enterprise use cases

    Google TAP tracks flake rate per test; quarantined tests don't block submit. Spotify's CI assigns ownership via backstage catalog entity. Netflix ChaosMonkey-adjacent CI practice: re-run failed tests 3x before declaring flake — reduces false quarantine.

    • Merge queue: failure blocks queue — triage SLA stricter (30 min) than optional PR CI.
    • Monorepo: failed Nx project maps to team via project.json tags → automatic routing.
    • Compliance: audit log of who quarantined test and when — flakes aren't silent quality debt.

    Production case study

    SaaS company (60 engineers) reduced ignored-red-build culture. Quarantine file grew to 89 tests without SLA — platform reset: 7-day expiry, auto-fail PR adding quarantine without ticket, bisect runbook in onboarding.

    • Problem: 38% re-run rate; engineers merged despite red optional checks.
    • Ownership: CODEOWNERS + #ci-failures Slack with 30 min ack SLA for main.
    • Flakes: quarantine/ dir; required CI excludes it; weekly flake burndown meeting.
    • Bisect: scripts/ci-check.sh used in 4 incidents; avg 12 commits narrowed in 20 min.

    Trade-offs

    • Quarantine: unblocks velocity; risk if quarantine becomes permanent parking lot.
    • Zero tolerance flakes: high quality bar; blocks merges during infra instability.
    • Auto re-run (3x): reduces false positives; hides intermittent product bugs if always passes on retry.
    • Manual triage: flexible; doesn't scale past ~20 PRs/day without dedicated platform on-call.

    Security implications

    Triage shortcuts can bypass security — quarantining SAST or signing steps is never acceptable.

    • Quarantine allowlist excludes security scan, license check, and secret scan jobs — platform-enforced.
    • Bisect scripts must not pull unverified dependencies — use lockfile-pinned npm ci inside bisect run.
    • Failed security job = P1 route to security team, not default dev owner quarantine path.
    • Audit quarantine PRs — malicious actor could quarantine tests covering their backdoor.

    Scalability analysis

    At high PR volume, failure notification noise and quarantine debt compound without automation.

    • Central flake registry across repos — dedupe same upstream flake in 50 services.
    • ML-based flake detection (Buildkite, Launchable) required above 500 runs/day manual triage.
    • Bisect on 10K commit range expensive — narrow with affected path log first.
    • Cross-timezone ownership — follow-the-sun unless merge queue forces single-region SLA.

    Staff engineer insights

    • If re-run without code change fixes CI, it's a flake — track it before it trains ignore-red behavior.
    • Quarantine without expiry is technical debt with interest — auto-expire or auto-escalate.
    • Bisect only works with fast repro script — invest in scripts/ci-check.sh before incidents.
    • Platform owns infra failures; product owns test failures — classify in first 5 minutes.

    Best practices

    • One-command minimal repro script committed in repo for bisect.
    • Quarantine list in git with owner, date, ticket link, expiry.
    • Never quarantine security or migration test jobs.
    • Track flake rate weekly; exec dashboard if rate >5%.

    Common mistakes

    • Re-run until green without filing flake — teaches org to distrust CI.
    • Quarantine entire job instead of single test — hides real failures.
    • Bisect without fetch-depth or on squashed history only — wrong result.

    Advanced interview questions

    Interview Prep

    Practice concise answers, then expand each card for the explanation.

    5 questions
    1AdvancedQuestionDesign flake quarantine that doesn't erode quality bar.+

    Answer

    Quarantined tests run in separate non-required workflow; tracked in git with 7-day SLA and ticket; security tests exempt; weekly burndown; flake rate metric; auto-remove quarantine if test passes 50 consecutive main runs.

    Follow-up

    Who approves quarantine PR?
    2AdvancedQuestionMain is red, 40 PRs blocked — triage steps?+

    Answer

    Identify failed job and SHA; check if infra (status.github.com, runner OOM); re-run once; if consistent, bisect between last green main SHA and HEAD; notify CODEOWNERS; communicate ETA in Slack; consider revert first if bisect long.

    Follow-up

    When revert vs fix forward?
    3AdvancedQuestionAutomate git bisect in CI — how?+

    Answer

    workflow_dispatch with inputs good/bad SHA; job runs git bisect run with scripts/ci-check.sh that exits 0/1; publish introducing commit as artifact; limit steps with --no-checkout optimizations; narrow range with path log first.

    Follow-up

    Monorepo bisect?
    4AdvancedQuestionCODEOWNERS doesn't match failed job — fix routing?+

    Answer

    Tag Nx/Bazel projects with owner metadata; failed job matrix includes project name; map via catalog; fallback @team-platform for .github and root config; periodic audit of orphan paths.

    Follow-up

    Cross-repo failures?
    5AdvancedQuestionMetrics to prove triage program success?+

    Answer

    Flake rate, median time-to-green after failure, re-runs per PR, % merges with any red check, main availability (merge queue unblocked %), MTTR for CI-caused prod incidents.

    Follow-up

    DORA linkage?

    Hands-on exercise

    Introduce a deliberate test failure, practice bisect to find it, and draft a quarantine entry with SLA.

    • Time bisect vs linear scan — document crossover point.
    • Write scripts/ci-check.sh usable in bisect run.
    • Simulate flake: random fail test — re-run 3x, classify.
    bash
    # Seed repo with 5 commits, one breaks test
    git bisect start
    git bisect bad HEAD
    git bisect good HEAD~4
    git bisect run npm test -- --testPathPattern=unit/smoke
    # After finding commit, revert or fix
    git bisect reset
    # Quarantine template (quarantine/README.md):
    # | Test | Owner | Ticket | Added | Expires |
    # | auth flake | @team-id | JIRA-123 | 2024-03-01 | 2024-03-08 |

    Summary

    You can triage build failures through ownership routing, flake quarantine with SLA, and git bisect automation — keeping CI trustworthy so red means stop and fix, not re-run until lucky.

    Ready to mark this lesson complete?Track your journey across the entire course.