Tôi có một script chạy hằng tuần, quét bốn nguồn rồi gửi bảng xếp hạng qua Telegram. Tuần này tôi lấy lại dữ liệu thô và kiểm tra từng con số bằng code. Số thì đúng — nhưng có ba chỗ mà bản tin tự tin nói điều nó không đo được. Đây là cách chúng sinh ra và cách sửa.
Lỗi 1: “sao/ngày” thưởng cho repo mới sinh
Script chia số sao cho số ngày tồn tại để xếp hạng. Công thức này có một điểm mù: nó không phạt repo non. Một repo 811 sao ở tuổi 0,82 ngày hiện 985 sao/ngày — xếp trên repo 5.226 sao ở tuổi 5,79 ngày (902 sao/ngày). Người đọc bảng thấy word-atlas đứng trên aerial-tv và hiểu sai độ phổ biến.
Mức độ: 25 trong 615 repo (4%) chưa đầy một ngày tuổi — đúng nhóm được công thức thưởng nhiều nhất. Repo non nhất có 43 sao nhưng vẫn hiện 58 sao/ngày.
Cách sửa: lọc tuổi tối thiểu trước khi chia, hoặc trả về hai bảng riêng — một bảng theo sao thô, một bảng tốc độ có ghi rõ tuổi từng repo. Đừng trộn hai đại lượng khác thứ nguyên vào một số hạng duy nhất.
Lỗi 2: nhãn “7 ngày” gắn lên cửa sổ dữ liệu không phải 7 ngày
Hai nguồn không nhận tham số cửa sổ thời gian. Lobsters dùng `/hottest.json` — trả về 25 item nóng nhất hiện tại — và Product Hunt dùng feed Atom 50 entry mới nhất. Script gọi hai endpoint đó rồi dán nhãn “trong 7 ngày” lên kết quả.
- Lobsters: 25 item trải từ 1,1 giờ đến 55,4 giờ — tức 2,3 ngày, không phải 7. Nhãn sai gấp ba lần.
- Product Hunt: 50 entry trải 22,6 giờ đến 36,9 ngày. Chỉ 36 trong 50 entry nằm trong cửa sổ 7 ngày.
- Nghĩa là phần “Product Hunt” của bản tin trộn launch của tuần này với launch của tháng trước mà không nói gì.
Cách sửa: lấy thời điểm của item cũ nhất và ghi nhãn theo đó, chứ không ghi theo tham số truyền vào script. Hoặc parse `published` từ chính feed và lọc bỏ item ngoài cửa sổ. Lọc bỏ luôn là lựa chọn trung thực hơn.
Lỗi 3: nguồn không có trường dữ liệu mình cần, nhưng vẫn đưa vào báo cáo
Feed Atom của Product Hunt không có số vote. Tôi kiểm tra cả 50 entry: không entry nào chứa trường vote. Nhưng phần Product Hunt trong bản tin là một danh sách có thứ tự — người đọc mặc định hiểu thứ tự đó là theo mức độ quan tâm. Nó chỉ là thứ tự xuất hiện trong feed.
- Số vote thật cần Product Hunt GraphQL API, có key và hạn mức — không phải feed công khai.
- Trong lúc chưa có, đúng phải là: ghi rõ “thứ tự theo feed, không theo vote”, hoặc bỏ hẳn thứ hạng.
- Tương tự, mục Show HN hiện cả điểm và số bình luận — nhưng bình luận không nằm trong công thức xếp hạng. Một bài 938 điểm, 425 bình luận và một bài 126 điểm, 36 bình luận được xếp bằng cùng một biến.
Một lỗi nữa tôi tìm được khi viết code kiểm tra
Khi đếm commit và số người đóng góp qua header `Link` của GitHub API, tôi parse bằng `split("page=")`. Chuỗi đó khớp cả `per_page=1` trong URL, nên mọi repo đều trả về 1 commit và 1 contributor — đọc lên có vẻ như mọi dự án đều là một người. Sửa lại bằng regex neo vào `[?&]page=` thì con số thật hiện ra: aerial-tv có 268 commit và 22 người đóng góp, harp có 2.565 commit và 171 người.
Điểm chung của bốn lỗi: bản tin tự tin ở chỗ nó chỉ *suy ra*, không đo. Số liệu tôi đếm trực tiếp thì đúng; nhãn, thứ hạng và đại lượng phái sinh thì sai. Đây là lý do tôi viết một file `verify_numbers.py` với assert cho từng con số trước khi đăng bài — nó bắt được cả bốn lỗi này.
Dữ liệu đã kiểm chứng: 615 repo mới của tuần và bao nhiêu trong đó bán được. Vì sao sao GitHub không đo được chất lượng dự án: bài tiếp theo.
Nguồn: Lobsters hottest API, Product Hunt feed, HN Algolia API, GitHub pagination. Dữ liệu chốt 26/10/2026.