Hướng dẫn
Flaky hay lỗi thật? Chẩn đoán CI đỏ
Mục tiêu
Build CI vừa đỏ. Trước khi sửa bất kỳ dòng code nào, hãy xác định đó là lỗi regression thật, test flaky, hay hạ tầng hỏng — bằng các lệnh có thể copy, output mẫu, và một kết luận rõ ràng cuối mỗi bước.
Vì sao quan trọng
Build đỏ không đồng nghĩa với bug thật. Đội ngũ testing của Google báo cáo năm 2016 rằng gần 16% số test của họ có mức flakiness nào đó, khoảng 1,5% tổng số lần chạy test cho kết quả flaky, và — con số nên thay đổi cách bạn chẩn đoán — khoảng 84% các lần test chuyển từ pass sang fail liên quan tới test flaky, không phải regression thật. Microsoft cho rằng khoảng 25% các lần CI fail là do test flaky, các nghiên cứu khác ghi nhận tới 50–56% trước khi khắc phục (Amikoo, tháng 10/2026). Nghiên cứu trên IEEE Transactions on Software Engineering năm 2026 cho thấy các lần fail flaky không bị phát hiện chiếm 9,8%–16,3% số lần chạy pipeline fail trong các dự án được nghiên cứu.
Nguy hiểm thật sự không phải là nhiễu — mà là điều nhiễu dạy đội ngũ của bạn. Kỹ sư Google viết rằng "khá phổ biến khi bỏ qua các lỗi thật trong test flaky do quá nhiều false positive," và rằng con người thường có xu hướng phớt lờ cảnh báo khi nó hay báo sai. Một khi lập trình viên quen chạy lại build đỏ mà không điều tra, regression thật bắt đầu trông y hệt như nhiễu.
Flaky, lỗi thật hay hạ tầng?
Dùng bảng quyết định này trên log của build fail trước khi chạm vào code:
- Regression thật — test fail ổn định trên một commit mà trước đây nó từng pass, và phần còn lại của log không có gì lạ. Tên test, lỗi, và dòng lỗi giống nhau ở mọi lần chạy lại.
- Flaky — test vừa pass vừa fail trên cùng một commit mà không có thay đổi code, hoặc đảo qua đảo lại thường xuyên qua các lần chạy gần đây. Định nghĩa của Google đúng là thế này: một test vừa có kết quả pass vừa có kết quả fail với cùng một đoạn code.
- Hạ tầng — lỗi không nằm ở code của bạn:
npm install/pip installfail, runner hết bộ nhớ (OOM kill), mạng timeout khi tải, trình biên dịch crash. Log chết trước khi test kịp chạy.
Hai cái bẫy: đừng tin một lần chạy lại thủ công mà chuyển xanh — một lần retry thành công không chứng minh gì về nguyên nhân gốc và chỉ chôn vùi flake cho lần sau. Và đừng xóa test mà bạn nghi là flaky — hãy cách ly nó (xem bên dưới) để tín hiệu không mất.
Chẩn đoán
- Lưu toàn bộ log build ra file — không thể chẩn đoán thứ bạn không đọc lại được:
npm test 2>&1 | tee ci.logKết quả mong đợi: toàn bộ output test hiển thị trên terminal và lưu vào
ci.log. Trên GitHub Actions, thêm một step sau lệnh test:- name: Run tests run: npm test 2>&1 | tee ci.log - Tìm lỗi thật đầu tiên — mọi thứ sau nó thường chỉ là hệ quả. Quét log theo marker của framework:
grep -n -m1 '●' ci.log # lỗi đầu tiên của jest grep -n -m1 'FAILED' ci.log # lỗi đầu tiên của pytestKết quả mong đợi: một số dòng chỉ vào test fail đầu tiên, ví dụ
47: ● Cart pricing › applies bulk discount. Nếu log chết ởnpm install, build Docker, hoặc bước biên dịch thì đây là hạ tầng — bỏ qua các bước còn lại và sửa runner. - Kiểm tra lịch sử của test fail trên cùng commit. Nó đã từng pass ở đây chưa? Một lần fail trên commit không thay đổi là bằng chứng flake mạnh nhất:
cifd record --history .cifd-history.jsonl --commit $GITHUB_SHA < ci.log cifd historyKết quả mong đợi: mỗi lần chạy được ghi lại kèm verdict. Một test fail ở
abc1234nhưng trước đó từng pass trên đúng commit đó là flake kinh điển — không có thay đổi code ở giữa, nên code không thể là nguyên nhân. - Chạy chẩn đoán tự động — lỗi đầu tiên, verdict, và các lần lặp lại trong một báo cáo:
npx ci-failure-detective analyze ci.log --format mdOutput mẫu (từ demo của chính công cụ trên log fixture thật):
## 🟡 CI Failure Detective — flaky **Category:** Test failure **Framework:** jest **Signature:** `a3f9c1e2b4d6` — seen 12× before (first 2026-10-18) **First error** (line 47): ``` ● Cart pricing › applies bulk discount over 10 items ``` **Failing tests:** - `Cart pricing › applies bulk discount over 10 items` (src/cart/pricing.test.js) — **🟡 flaky** - Failed on abc1234 but passed on the same commit before — classic flake, no code change in between. **Suggested next steps** (advisory only — nothing is applied automatically): - **1 flaky test — quarantine suggested (not applied)** - Consider quarantining via --testPathIgnorePatterns or jest.retryTimes - Từ nay hãy chạy test ở chế độ verbose để các lần chẩn đoán sau có bằng chứng. Phát hiện flake cùng-commit cần bản ghi pass của từng test, và log thường chỉ ghi lại các lần fail:
npx jest --verbose # thay cho npx jest pytest -v # thay cho pytestVới log verbose, một test pass trên một commit sẽ được nhìn thấy; không có nó, bằng chứng cùng-commit không tồn tại và verdict phải dựa vào tần suất đảo và quy tắc regression.
Chính sách cách ly
Khi đã biết một test là flaky, bạn làm gì với nó? Ba thực tiễn từ các đội ngũ lớn:
- Cách ly, không phớt lờ, không xóa. Google đánh dấu test flaky để nó chỉ báo fail khi fail 3 lần liên tiếp, và các đội chỉ chạy lại những test fail thay vì toàn bộ suite. Danh sách cách ly là một khoản nợ kỹ thuật được theo dõi và phải co lại — một test flaky rời khỏi pipeline chặn cho tới khi nguyên nhân gốc được sửa.
- Sửa nguyên nhân gốc, không sửa cái còi báo. Các nguồn phổ biến nhất là vấn đề timing/đồng bộ (async wait), race condition trong code đồng thời, state chia sẻ giữa các test, phụ thuộc thứ tự chạy test, hạ tầng drift, và dịch vụ bên thứ ba không ổn định. Thay sleep cố định bằng chờ theo điều kiện, cô lập state chia sẻ, đóng băng đồng hồ, cố định seed random.
- Chạy lại tự động là giải pháp cuối cùng, không phải đầu tiên. Các plugin như
pytest-rerunfailureshayjest.retryTimesthử lại một test fail một số lần cố định và báo thành công nếu một lần pass. Chúng che triệu chứng mà không sửa nguyên nhân, và chúng dạy cả đội rằng xanh-sau-retry nghĩa là "ổn" — đó là cách các lỗi thật bắt đầu ẩn nấp.
Lời cảnh báo của Martin Fowler năm 2011 vẫn còn nguyên giá trị: nếu không kiểm soát, các test không xác định có thể phá hủy hoàn toàn giá trị của cả bộ regression tự động.
Giới hạn
Không phương pháp phát hiện nào hoàn hảo: một test tháng mới flake một lần có thể pass 100 lần chạy sạch rồi vẫn fail đúng ngày release — phát hiện là câu hỏi về lịch sử, không phải về một lần chạy. Một test flaky cũng có thể là tín hiệu, không phải nhiễu: một test thỉnh thoảng timeout có thể đang bắt được một service chậm thật sự. Hãy điều tra trước khi cách ly.
Công cụ của chúng tôi
ci-failure-detective là bước chẩn đoán local-first của chúng tôi: đọc log CI fail và báo lỗi thật đầu tiên, verdict cho từng test — flaky, real, hoặc unknown kèm điểm tin cậy — và gom các lần lặp lại theo chữ ký fail ổn định dài 12 ký tự. Một test được gọi là flaky khi nó vừa fail vừa pass trên cùng một commit (tin cậy 0,95) hoặc đảo thường xuyên qua các lần chạy (0,70); một test fail ổn định ở nơi nó từng pass là regression thật (0,80). Không có lịch sử, nó trả lời unknown — không bao giờ đoán. Công cụ phân tích Jest, pytest, go test, Mocha, và khả năng phân loại fail hoạt động trên mọi log thô: lỗi npm/pip/go install, lỗi tsc, build Docker, OOM kill. Nó chạy trong runner của bạn — không byte log nào rời khỏi repo, không cần đăng ký. Các hành động đề xuất (gợi ý cách ly, hướng dẫn retry) chỉ mang tính tư vấn: cách ly luôn ở chế độ gợi ý theo thiết kế, vì tự động retry hoặc tắt fail sẽ giấu các bug thật.
So sánh trung thực (kiểm tra 2026-10-24): BuildPulse, Trunk, Datadog và Mergify (18–99$/tháng) cho bạn dashboard, phân tích xu hướng, tự động cách ly và cảnh báo — đổi lại bạn phải tải toàn bộ dữ liệu test và log lên cloud của họ. Gradle Develocity thêm gom nhóm fail bằng AI và chọn test dự đoán, theo mô hình enterprise. Các đối thủ open-source gần nhất là gh-failmap (gom nhóm fail dạng CLI, nhưng chỉ GitHub và không có verdict flaky) và flakestat (săn flake local-first, nhưng chỉ săn flake — không gom nhóm, không đọc log). Các công cụ chẩn đoán bằng LLM gửi log của bạn tới API bên thứ ba và tính phí theo lần chạy. CI Failure Detective không có dashboard, không có cảnh báo — nó là một bước chẩn đoán local, không phải một nền tảng.
Nguồn
- Google Testing Blog, "Flaky Tests at Google and How We Mitigate Them" (tháng 5/2016) — testing.googleblog.com
- Amikoo, "Real Bug or Flaky Test? How to Diagnose Failures Before You Fix Anything" (tháng 10/2026) — amikoo.ai
- Qable, "Regression Testing Strategy: What to Re-Run in 2026" (tháng 10/2026), trích dẫn Memon et al., "Taming Google-Scale Continuous Testing" (ICSE-SEIP 2017) — qable.io
- ci-failure-detective README, techdevfun/ci-failure-detective, phần so sánh trung thực (kiểm tra 2026-10-24)
Cập nhật lần cuối: 2026-10-31. Sửa trên GitHub · Báo lỗi
← Hướng dẫn trước · Hướng dẫn tiếp →
Vẫn kẹt? Nhận hỗ trợ triển khai →