Skip to content

Day 21|報過的 Bug 別再開單,誤報也要記下來 ​

前言 ​

Day 20 的分數只管單筆。可是跑多輪之後,同一個症狀會被重複撿到,判錯的那幾筆也會一再回頭。今天處理兩件事:確認過的誤判記進名單別再報,同一個指紋認得出來就不開第二張單。

為什麼需要去重與誤報名單 ​

Day 20 的分數解決「哪一筆先看」,但它一次只看一筆。跑到第四輪你會發現另一個問題:同一個 bug 被撿到四次,四筆各自都拿到 high,分數看不出它們其實是同一件事。

本專案的索引現在是 18 張單,背後是 34 次觀察。不去重的話,issue tracker 收到的是 34 張,其中有 4 張都在講數量欄。開發者打開 tracker 第一件事不是修 bug,是先花時間判斷哪幾張是同一張。

誤報是另一個方向的浪費。有些症狀已經有人拍板過不是缺陷,例如未登入頁面打 /users/me 回 401。沒有名單,它每一輪都會被重新撿起來、重新算分、重新送到你面前,你每一輪都要重新判同一件事。

兩件事的目標是同一個:讓 tracker 裡的每一張單都值得打開。誤報率一旦上去,人就不再信任這個來源,後面所有的自動化都失去意義。

實驗 ​

拿累積下來的真檔驗一次。先算指紋:把 cart id、時間戳、第幾次全部正規化掉,一筆「購物車列小計顯示 $00.00、頁尾總計正確」的觀察,算出 cart|line-item-total-shows-zero|view-cart。拿去查表:

bash
grep -A3 'cart|line-item-total-shows-zero|view-cart' output/issues-index.yaml
yaml
- fingerprint: "cart|line-item-total-shows-zero|view-cart"
  issue: https://github.com/vansleee/sdet-skills/issues/2
  occurrences: 2

完全相同,併進舊單、occurrences 加一,不開新單。再盤點整份索引:

bash
python3 -c "
import re
occ = [int(x) for x in re.findall(r'occurrences: (\d+)', open('output/issues-index.yaml').read())]
print('單數', len(occ), '觀察總數', sum(occ))
"
單數 18 觀察總數 34

34 次觀察壓成 18 張單,16 次重複沒有變成第二張單。

指紋:格式、正規化、比對四規則 ​

指紋的工作只有一句話:讓同一個 bug 的不同次觀察算出同一個字串,不同 bug 算出不同字串。格式固定三段:

<area>|<signature>|<trigger>

area 是行為區域,用路由或功能名,去掉 query string 與 id;signature 是錯誤簽章或一句穩定的現象描述,不含 stack trace 與行號;trigger 是觸發條件的「類別」,寫成動詞加名詞,描述做了什麼類型的事而不是具體值。全部小寫、以 - 連字。

上半是兩筆不同寫法的觀察正規化成同一個指紋,下半是 C-04 與 C-05 同 area 不同 signature 所以標 related 不合併

正規化把「看起來不一樣」的合起來,簽章把「看起來很像」的分開。兩件事用同一組欄位。

算之前要先正規化:UUID 與 hash 換成 <id>、具體數字換成 <n>、時間戳與 session id 直接移除、大小寫統一。但分類性的值要保留,qty-0 與 qty-negative 是兩個不同的 bug,不能一起換成 qty-<n>。

有一份東西明令不准放進指紋:出現次數、時間戳、cart id、截圖路徑、瀏覽器版本、測試帳號、環境名稱。這些屬於觀察紀錄,掛在 issue 的證據上。放進去的後果是同一個 bug 每次算出來的指紋都不一樣,永遠去不了重。

算完拿去比對,四條規則分流:

比對結果處置
完全相同不開新單,舊單 occurrences 加一、新證據 append、必要時更新 confidence
area 與 signature 相同、trigger 不同標 related,不自動合併,列給人判
找不到才開新單,並把指紋寫進索引
命中誤報名單移進 suppressed,記下命中哪一條

鬆緊拿捏只有一個判準:只放「換一次觀察也不會變的本質」。 太鬆會把不同 bug 併成一張,開發者看不懂;太緊會讓去重完全失效。

兩份狀態檔 ​

去重與濾誤報是跨輪累積的能力,所以兩份檔案都放在 output/ 根目錄,不進單輪的 session 資料夾。切進單輪就失去去重與校準的能力。

output/issues-index.yaml 一筆一張單,欄位是指紋、issue 連結、本地報告路徑、occurrences、confidence、target、證據清單,疑似同根因的另外掛 related。它同時是「這個 bug 被撿到幾次」的計數器,Day 20 的複現次數配分就是讀它。

output/known-false-positives.yaml 一筆一條 pattern,欄位是 pattern、scope、reason、decided_by、decided_at。後兩個欄位不是形式,進這份清單等於有人拍板過。所以有一條硬規則,needs-spec 不准寫進來。沒有規格可比對只代表還沒人判,不代表它不是 bug,寫進去就是把一個未決的問題永久消音。

scope 那欄也不是裝飾。本專案第一條 FP 的 scope 是 anonymous-only,後來有一個登入後才發生、且以 API 回 200 為判準的候選,閘門明確記下「不在該條範圍」,沒有被誤壓。

小結 ​

一筆新觀察進來,先正規化掉會變動的部分,算出三段式指紋;拿指紋先過誤報名單,命中就壓下並記下命中哪一條;沒命中再查索引,完全相同就併進舊單、area 與 signature 相同但 trigger 不同就標 related 交人判、找不到才開新單並寫回索引。

              一筆新觀察
                   │
                   ▼
      正規化(id / 時間戳 / 次數 移除)
                   │
                   ▼
        算指紋 area|signature|trigger
                   │
                   ▼
      查 known-false-positives.yaml
            │             │
          命中           未命中
            ▼             ▼
        suppressed   查 issues-index.yaml
                           │
        ┌──────────────────┼──────────────────┐
        ▼                  ▼                  ▼
     完全相同        area+signature 同      找不到
                      trigger 不同
        ▼                  ▼                  ▼
  occurrences++         標 related          開新單
  證據 append           交人判              寫回索引

去重是把重複變成信心,不是把單子變多,同一個 bug 出現四次應該讓它的分數更高,而不是讓 tracker 多三張。誤報名單的門檻是有人拍板,decided_by 與 decided_at 缺一不可,needs-spec 一律不准進。而規則擋得掉機械性重複,擋不掉同根因,所以第二條規則寫的是列給人判,不是自動合併。

下一步 ​

判斷力到今天湊齊了:Day 18 的 oracle 決定是不是 bug、Day 20 的分數決定值不值得往下花力氣、今天的指紋與誤報名單決定該不該再報一次。但這三塊還是得你一條一條下指令去觸發。明天把它們包成第一位代理人:給一張 charter,它自己探索、分類、判定、打分、去重、濾誤報,交回一份排好序的候選清單。只找,不開單。


參考資料 ​

  1. Ivan P. Fellegi、Alan B. Sunter(1969)《A Theory for Record Linkage》,Journal of the American Statistical Association 64(328) - record linkage 的理論本體:不同來源的觀察怎麼判定指向同一個實體,指紋去重就是它的簡化版
  2. Per Runeson、Magnus Alexandersson、Oskar Nyholm(2007)《Detection of Duplicate Defect Reports Using Natural Language Processing》,ICSE 2007 - 用自然語言相似度找重複缺陷報告,跟本篇用結構化指紋是兩條不同路線,對照著看很清楚
  3. Nicholas Jalbert、Westley Weimer(2008)《Automated Duplicate Detection for Bug Tracking Systems》,DSN 2008 - 把去重當成分類問題,量化「漏併」與「誤併」的代價,正是指紋鬆緊拿捏那一段的依據
  4. Kirk Glerum et al.(2009)《Debugging in the (Very) Large: Ten Years of Implementation and Experience》,SOSP 2009 - Windows Error Reporting 的 bucketing:工業規模下靠簽章把海量崩潰壓成可處理的桶,<area>|<signature>|<trigger> 是同一個思路
  5. Al Bessey et al.(2010)《A Few Billion Lines of Code Later: Using Static Analysis to Find Bugs in the Real World》,Communications of the ACM 53(2) - 誤報率一旦上去,工程師就不再信這個工具,這是誤報名單要存在的理由
  6. 本專案 references/bug-fingerprint.md(指紋格式、正規化與比對四規則)、state-templates/known-false-positives.example.yaml 與 issues-index.example.yaml - 兩份狀態檔的欄位定義

作者備忘錄 (Author's Notes) — 正式出版前應移除

本區塊為作者內部檢討區,用於追蹤內容品質與後續改進方向。正式出版前應移除或轉為附錄。

章節大綱

  1. 前言 — 承接 Day 20:分數只管單筆,跑多輪後同一症狀被重複撿到、判錯的一再回頭,今天處理去重與誤報名單兩件事。
  2. 為什麼需要去重與誤報名單 — 同一 bug 被撿四次各拿 high,分數看不出是同一件事;本專案 18 張單背後是 34 次觀察,不去重 tracker 會塞 34 張;誤報名單防止已拍板的症狀每輪重新算分;目標是讓 tracker 每張單都值得打開。
  3. 實驗 — 正規化計算指紋範例(cart id、時間戳、次數都去掉);查表命中完全相同、occurrences 加一不開新單;整份索引盤點:34 次觀察壓成 18 張單。
  4. 指紋:格式、正規化、比對四規則 — 三段式格式 <area>|<signature>|<trigger>;正規化規則(UUID/數字/時間戳移除,但分類性的值如 qty-0 與 qty-negative 要保留不可一起換成 qty-<n>);明令不准放進指紋的欄位清單;四條比對規則分流表(完全相同/trigger 不同標 related/找不到開新單/命中誤報名單移進 suppressed);鬆緊判準:只放「換一次觀察也不會變的本質」。
  5. 兩份狀態檔 — issues-index.yaml(跨輪累積,放在 output 根目錄不進單輪資料夾);known-false-positives.yaml 含 decided_by/decided_at 兩欄,needs-spec 一律不准寫入;scope 欄位案例:登入後才發生的候選未被誤壓。
  6. 小結 — 流程圖:正規化→算指紋→查誤報名單→查索引→三分流(完全相同/related/找不到);強調「去重是把重複變成信心,不是把單子變多」「規則擋不掉同根因,第二條寫的是列給人判不是自動合併」。
  7. 下一步 — 預告 Day 22:oracle/分數/指紋誤報名單三塊判斷力湊齊,但仍要人逐條下指令觸發,明天包成第一位代理人。
  8. 參考資料 — Fellegi & Sunter record linkage 理論、Runeson 重複缺陷報告偵測、Jalbert & Weimer 去重當分類問題、Glerum et al. Windows Error Reporting bucketing、Bessey et al. 靜態分析誤報成本、專案內 bug-fingerprint.md 與範本檔。

結構特徵:延續 Day 20 的信心指數主題,把「單筆判斷」擴展到「跨輪累積」的視角;用真實索引數字(34→18)具體量化去重效益,四規則表與兩份狀態檔的欄位設計是全書「留痕勝於自律」原則在去重場景的落地,直接鋪墊 Day 22 的代理人化收束。