비결정성과 Flake 관리
flaky를 타이밍 문제가 아니라 환경·상태·외부 의존성·모델 분산의 운영 신호로 다루는 방법
flake는 테스트의 속성이 아니라 비결정성이 새어 나오는 방식입니다. 문제는 flaky가 존재한다는 사실보다, 그것을 product, data, infra, external, model variance와 구분하지 못하는 운영 체계에 있습니다.
비결정성의 정의
같은 코드와 같은 의도인데도 결과가 간헐적으로 달라진다면, 그건 대개 테스트 코드가 아니라 환경과 상태 관리가 실패했다는 신호입니다.
단, 아래는 flaky와 구분해야 합니다.
- 환경 준비 자체가 실패한 경우
- seed / reset 실패
- third-party sandbox 장애
- 실제 제품 회귀
taxonomy
| 분류 | 예시 | 보통의 근본 원인 |
|---|---|---|
| timing | 요소는 보이지만 상태 전환이 늦음 | wait 전략 부족, animation, polling |
| locator / contract | 가끔 다른 요소를 잡음 | 불안정 selector, contract drift |
| data | 순서와 상태가 뒤엉킴 | 공유 계정, cleanup 누락, race condition |
| environment | 특정 시간대에만 실패 | staging 충돌, quota, infra noise |
| external dependency | provider 응답이 흔들림 | sandbox 불안정, rate limit |
| async / eventual consistency | queue 완료 시점이 랜덤 | worker backlog, webhook delay |
| model variance | LLM/agent 결과가 달라짐 | prompt drift, model version, tool timing |
운영 원칙
- flaky는
"known flaky"로 방치하지 않고 상태를 가진 항목으로 관리합니다. - gate에 영향을 주는 비결정성은 즉시 quarantine 또는 blocker 재분류가 필요합니다.
- retry는 flaky 탐지 힌트일 뿐, 해결로 간주하지 않습니다.
- 같은 증상이더라도 environment/data/model variance를 따로 라벨링합니다.
quarantine 정책
| 항목 | 기본 규칙 |
|---|---|
| quarantine 조건 | 7일 내 2회 이상 간헐 실패, 또는 flake rate 2% 초과 |
| owner | suite owner + QA platform |
| 처리 기한 | critical은 48시간, full은 5영업일 |
| gate 영향 | gate suite에서는 quarantine 여부를 release manager가 승인 |
lifecycle
탐지 -> 분류 -> quarantine 여부 결정 -> 원인 수정 -> 재활성 -> 2주 추적자동 분류 파이프라인
gate를 깨는 flaky는 사람이 로그를 수동으로 읽기 전에, 분류와 owner 추정이 먼저 붙는 흐름이 필요합니다.
브라우저 러너 정규화 예시
Playwright 같은 browser runner는 보통 JSON report를 공통 attempt schema로 맞추는 단계가 필요합니다.
npx playwright test --reporter=json > artifacts/playwright/results.jsontype Attempt = {
testId: string
title: string
classification: 'timing' | 'locator' | 'data' | 'environment' | 'external'
}중요한 건 Playwright JSON 자체가 아니라, 모든 runner가 같은 failure vocabulary로 정규화되어야 한다는 점입니다.
anti-pattern
- retry를 올려서 flaky를 덮는 문화
- shared integration 충돌을 product bug처럼 다루는 운영
- queue 지연과 UI timing issue를 같은 bucket에 넣는 분류 체계
- 모델/프롬프트 변경이 있는 agent workflow인데 variance 추적 필드를 남기지 않는 구조