Mở đầu — vì sao bài này quan trọng
Bạn đã đi qua hơn nửa khóa học. Bạn biết JQL cơ bản (Bài 7), JQL nâng cao (Bài 8), JQL Functions (Bài 38). Nhưng có một sự thật phũ phàng mà mình muốn nói thẳng: biết cú pháp JQL và thực sự dùng được JQL trong công việc hằng ngày là hai chuyện hoàn toàn khác nhau.
Mình từng phỏng vấn một bạn BA có 3 năm kinh nghiệm. Bạn ấy thuộc làu lý thuyết JQL, đọc vanh vách ORDER BY, IN, WAS. Nhưng khi mình đưa ra một yêu cầu thực tế — "Tìm cho anh tất cả các story đang assign cho team Payment, chưa done, đã quá due date, sắp xếp theo priority" — bạn ấy loay hoay mất 10 phút và viết sai. Vấn đề không phải bạn ấy dốt, mà là bạn ấy chưa bao giờ luyện tập đủ nhiều để biến JQL thành phản xạ.
Bài này không dạy thêm cú pháp mới. Bài này là một phòng tập (lab). Bạn sẽ ngồi xuống, mở JIRA lên, và viết query thật, lưu filter thật, xây dựng một Filter Library — một thư viện filter chuẩn mà bạn có thể tái sử dụng suốt sự nghiệp BA của mình. Đây là bài mà bạn phải để tay lên bàn phím, không phải chỉ đọc cho biết.
Một BA giỏi không phải người nhớ nhiều cú pháp nhất. Đó là người mở JIRA ra, gõ vài dòng, và lập tức có đúng danh sách issue mình cần — trong khi đồng nghiệp vẫn đang scroll backlog bằng tay.
Khái niệm cốt lõi
Lab là gì và tại sao phải làm thật
Lab (phòng thực hành) khác bài giảng ở chỗ: bạn không tiếp thu thông tin một chiều, mà tạo ra kết quả. Trong bài này, mỗi bài tập đều có một câu hỏi nghiệp vụ cụ thể, và nhiệm vụ của bạn là dịch nó sang JQL, chạy thử, kiểm tra kết quả đúng/sai.
Filter trong JIRA — không chỉ là một câu query
Khi bạn viết một câu JQL và bấm lưu, JIRA tạo ra một Filter (bộ lọc) — một đối tượng có tên, có chủ sở hữu, có quyền chia sẻ. Filter này quan trọng hơn bạn tưởng:
- Nó là nguồn dữ liệu cho Dashboard gadget (Bài 10), cho Board (Bài 9), cho Report.
- Nó có thể chia sẻ cho cả team, để mọi người cùng nhìn một danh sách thống nhất.
- Nó có thể subscribe — JIRA tự gửi email kết quả filter theo lịch (hằng ngày, hằng tuần).
Filter Library — tài sản nghề nghiệp của BA
Filter Library là một bộ sưu tập các filter chuẩn, được đặt tên có hệ thống, phục vụ các nhu cầu lặp đi lặp lại. Ví dụ một BA thường cần:
- "Việc của tôi chưa xong" (theo dõi công việc cá nhân)
- "Story thiếu Acceptance Criteria" (kiểm soát chất lượng backlog)
- "Bug nghiêm trọng chưa được xử lý trong sprint này" (báo cáo rủi ro)
- "Issue đã quá due date" (cảnh báo trễ hạn)
Quy ước đặt tên filter — đừng bỏ qua
Khi một team có 50 filter mà tên kiểu "filter 1", "test", "abc" thì coi như cả thư viện thành rác. Mình khuyên một quy ước đơn giản:
[Phạm vi] - [Đối tượng] - [Trạng thái/Mục đích]
Ví dụ: [PAY] - Stories - Missing AC, [Personal] - My Open Issues, [QA] - Critical Bugs - Current Sprint. Tên rõ ràng giúp người khác (và chính bạn 3 tháng sau) hiểu ngay filter làm gì.
Tình huống thực tế
Ví dụ 1: Tiki và bài toán "backlog mù"
Một team phát triển tính năng thanh toán tại Tiki (giả định bối cảnh, quy mô khoảng 12 người: 2 BA, 6 dev, 2 QA, 1 PO, 1 SM) gặp vấn đề: project có hơn 800 issue active, và mỗi buổi standup mất 15-20 phút chỉ để hỏi "issue nào đang kẹt?".
Chị Lan — BA trưởng — quyết định xây Filter Library. Chị tạo 5 filter cốt lõi:
[PAY] - In Progress > 3 days— việc đang làm dở quá 3 ngày (dấu hiệu bị kẹt)[PAY] - Blocked— issue có status Blocked[PAY] - No Assignee— issue chưa ai nhận[PAY] - Overdue— quá due date[PAY] - Ready for Review— chờ review
project = PAY AND status = "In Progress" AND status CHANGED TO "In Progress" BEFORE -3d ORDER BY updated ASC
Bài học rút ra: Sau 2 tuần, standup rút xuống còn 7 phút. Cả team nhìn chung 5 filter trên một dashboard, không ai phải scroll backlog bằng tay nữa. Điểm mấu chốt: chị Lan không viết query phức tạp một lần rồi quên — chị lưu thành filter có tên, chia sẻ cho cả team, biến kiến thức cá nhân thành công cụ tập thể.
Ví dụ 2: Anh Khoa và cú "filter đếm sai" tại một startup fintech ở Singapore
Anh Khoa, BA tại một startup fintech ở Singapore, được PO yêu cầu báo cáo: "Còn bao nhiêu story chưa estimate trong sprint tới?". Anh viết nhanh:
project = WALLET AND issuetype = Story AND "Story Points" = 0
Kết quả: 4 story. Anh báo cáo "chỉ còn 4 story chưa estimate". PO yên tâm lên kế hoạch. Nhưng đến sprint planning, hóa ra có tới 17 story chưa estimate. Sai ở đâu?
Vấn đề: những story chưa estimate thì field Story Points để trống (EMPTY), chứ không phải bằng 0. Query = 0 chỉ bắt được những story ai đó vô tình gõ số 0. Câu đúng phải là:
project = WALLET AND issuetype = Story AND "Story Points" IS EMPTY AND sprint in futureSprints()
Bài học rút ra: Phân biệt IS EMPTY (chưa có giá trị) và = 0 (có giá trị bằng 0) là một trong những lỗi kinh điển nhất. Khi xây Filter Library, mỗi filter phải được kiểm chứng — anh Khoa lẽ ra nên đối chiếu kết quả filter với một mẫu nhỏ kiểm tra bằng tay trước khi tin nó. Một filter sai còn nguy hiểm hơn không có filter, vì nó tạo cảm giác an toàn giả.
Ví dụ 3: Team BA của FPT Software và filter dùng chung bị "trôi"
Tại một dự án outsourcing của FPT Software (đội 8 BA phục vụ một khách hàng Nhật), team xây một Filter Library chung khá tốt. Nhưng 6 tháng sau, nhiều filter "bỗng dưng" trả về kết quả lạ. Lý do: các filter được lưu dưới tài khoản cá nhân của một bạn BA đã nghỉ việc, và khi tài khoản bị deactivate, một số filter share bị ảnh hưởng; ngoài ra nhiều filter hardcode tên sprint cũ như sprint = "Sprint 12".
Họ sửa lại theo hai nguyên tắc: (1) Filter dùng chung phải đặt owner là một group/tài khoản chức năng, không phải cá nhân; (2) Dùng hàm động như openSprints(), currentUser(), now() thay vì hardcode giá trị cố định.
Bài học rút ra: Filter Library là tài sản phải được bảo trì. Quyền sở hữu và việc dùng hàm động quyết định filter của bạn còn dùng được sau 6 tháng hay không.
Hướng dẫn từng bước
Bước 0: Chuẩn bị môi trường
Bạn cần một JIRA instance để gõ thật. Có hai lựa chọn:
- Dùng JIRA của công ty bạn — tốt nhất, vì dữ liệu thật, bài tập sát thực tế.
- Tạo bản dùng thử Cloud miễn phí — vào atlassian.com, đăng ký Jira Cloud free (tối đa 10 user). Tạo một project mẫu kiểu Scrum, tạo sẵn 15-20 issue đủ loại (Story, Bug, Task), gán linh tinh cho vài user, đặt vài due date.
Bước 1: Mở màn hình tìm kiếm nâng cao
Trên JIRA Cloud: vào Filters → View all filters → Advanced issue search, hoặc gõ trực tiếp đường dẫn /issues/?jql=. Đảm bảo bạn đang ở chế độ JQL (không phải Basic). Có một nút chuyển "Basic / JQL" ở góc — bấm vào JQL.
Bước 2: Viết — chạy — kiểm chứng (vòng lặp cốt lõi)
Với mỗi câu query, luôn làm đủ 3 bước:
- Viết câu JQL.
- Chạy (Enter) và xem kết quả.
- Kiểm chứng: số lượng có hợp lý không? Mở thử 1-2 issue xem có đúng tiêu chí không? Đây là bước hầu hết mọi người bỏ qua — và là bước quan trọng nhất.
Bước 3: Lưu thành Filter
Khi câu query đã đúng, bấm Save as, đặt tên theo quy ước [Phạm vi] - [Đối tượng] - [Mục đích]. Sau khi lưu, bấm Details để mở Edit permissions — chọn chia sẻ với group/project phù hợp.
Bước 4: Tổ chức Filter Library
Đặt các filter liên quan vào nhóm tên thống nhất (cùng prefix). Tạo một dashboard gom các filter quan trọng nhất qua gadget Filter Results. Cân nhắc Subscribe cho những filter cần theo dõi định kỳ (ví dụ filter "Overdue" gửi email mỗi sáng thứ Hai).
Bước 5: Bài lab có hướng dẫn — làm theo thứ tự
Lab 1 — Việc của tôi. Viết filter trả về tất cả issue đang assign cho bạn, chưa done:
assignee = currentUser() AND statusCategory != Done ORDER BY priority DESC, due ASC
Lưu tên [Personal] - My Open Issues. Dùng currentUser() để ai mở filter cũng thấy việc của chính họ.
Lab 2 — Story thiếu Acceptance Criteria. Giả sử AC lưu trong một custom field tên "Acceptance Criteria":
project = <KEY> AND issuetype = Story AND "Acceptance Criteria" IS EMPTY AND statusCategory != Done
Đây là filter "vàng" cho BA — bắt những story chưa đủ điều kiện đưa vào sprint.
Lab 3 — Bug nghiêm trọng trong sprint hiện tại.
project = <KEY> AND issuetype = Bug AND priority in (High, Highest) AND sprint in openSprints() AND statusCategory != Done ORDER BY priority DESC
Lab 4 — Issue quá hạn.
project = <KEY> AND due < now() AND statusCategory != Done ORDER BY due ASC
Lab 5 — Việc tôi tạo nhưng người khác làm dở.
reporter = currentUser() AND assignee != currentUser() AND status = "In Progress"
Lab 6 — Issue thay đổi gần đây (theo dõi biến động).
project = <KEY> AND updated >= -2d ORDER BY updated DESC
Sau khi làm xong 6 lab này, bạn đã có một Filter Library khởi đầu gồm 6 filter chuẩn — nền tảng để mở rộng.
Lỗi thường gặp & mẹo
Lỗi 1 — Nhầm = 0 với IS EMPTY. Như ví dụ anh Khoa: field trống không bằng 0. Luôn dùng IS EMPTY / IS NOT EMPTY để kiểm tra sự tồn tại của giá trị.
Lỗi 2 — Hardcode giá trị động. Viết sprint = "Sprint 23" hay assignee = "khang" khiến filter chết yểu. Dùng openSprints(), futureSprints(), currentUser(), now(), startOfWeek(), endOfMonth() để filter tự cập nhật.
Lỗi 3 — Nhầm status với statusCategory. Mỗi project có thể đặt tên status khác nhau ("Done", "Closed", "Resolved", "Hoàn thành"). Nếu bạn lọc status != Done mà project dùng "Closed", bạn sẽ sót issue. Dùng statusCategory != Done để bắt mọi status thuộc nhóm "đã xong", bất kể tên gọi.
Lỗi 4 — Quên ORDER BY. Một filter không sắp xếp khiến danh sách lộn xộn, khó dùng cho báo cáo. Luôn thêm ORDER BY theo tiêu chí có nghĩa (priority, due, updated).
Lỗi 5 — Tên custom field có dấu cách không đặt trong ngoặc kép. Viết Story Points = 5 sẽ lỗi; phải là "Story Points" = 5.
Lỗi 6 — Filter cá nhân nhưng share kết quả công khai. Nếu filter dùng currentUser() rồi đem share lên dashboard chung, mỗi người sẽ thấy kết quả khác nhau — đôi khi gây nhầm lẫn. Với dashboard chung của team, hãy dùng tiêu chí cố định (như assignee in membersOf("team-pay")) thay vì currentUser().
Mẹo 1 — Tận dụng auto-complete. Khi gõ JQL, JIRA gợi ý field và giá trị. Để con trỏ sau tên field rồi gõ — bạn sẽ thấy các toán tử và giá trị hợp lệ, đỡ sai cú pháp.
Mẹo 2 — Test trước khi share. Trước khi chia sẻ filter cho cả team, chạy thử và đối chiếu vài issue bằng tay. Filter sai gây hại hơn không có filter.
Mẹo 3 — Lưu cả filter "negative". Đôi khi filter tìm cái thiếu (story thiếu AC, issue chưa estimate, bug chưa gán) còn giá trị hơn filter tìm cái có. Đây chính là góc nhìn đặc trưng của BA về chất lượng dữ liệu.
Mẹo 4 — Đặt một filter làm "nguồn sự thật". Ví dụ filter "Scope của sprint này" dùng chung cho cả board lẫn report, đảm bảo mọi người nói cùng một con số.
Bài tập thực hành
Hãy hoàn thành trong JIRA thật (công ty hoặc bản trial). Nộp lại bằng cách chụp màn hình câu JQL + kết quả của từng filter.
Phần A — Xây Filter Library cơ bản (bắt buộc). Tạo và lưu đủ 6 filter từ Lab 1-6 ở trên, đặt tên đúng quy ước, chia sẻ với một group/project. Chụp lại danh sách filter trong "View all filters".
Phần B — Bài toán nghiệp vụ (tự viết JQL). Viết filter cho từng yêu cầu sau:
- Tất cả Bug được tạo trong tuần này, chưa được gán cho ai.
- Story đã done trong sprint vừa kết thúc nhưng không có Story Points (để truy vết estimate thiếu).
- Issue mà bạn đang là watcher, có priority High trở lên, chưa done.
- Tất cả Epic của project có ít nhất một child issue đang Blocked (gợi ý: dùng
issueFunctionnếu instance có ScriptRunner, hoặc tiếp cận hai bước). - Issue được cập nhật lần cuối cách đây hơn 14 ngày nhưng vẫn đang In Progress (việc "bỏ quên").
Phần D — Tự đánh giá thư viện. Mở lại 6 filter ở Phần A. Với mỗi filter, tự hỏi: nó có hardcode giá trị động nào không? Owner là cá nhân hay group? Nếu mình nghỉ việc, filter này còn chạy đúng không? Ghi lại và sửa những filter chưa đạt.
Tóm tắt
Bài lab này không cho bạn cú pháp mới — nó biến cú pháp bạn đã học thành phản xạ và tài sản. Ba điều cốt lõi cần khắc ghi:
- Vòng lặp Viết — Chạy — Kiểm chứng là kỷ luật bắt buộc. Một filter chưa kiểm chứng là một filter chưa hoàn thành. Câu chuyện anh Khoa đếm sai 4 thay vì 17 story cho thấy cái giá của việc tin filter mà không đối chiếu.
- Filter Library là tài sản nghề nghiệp. Đặt tên có hệ thống, chia sẻ cho team, dùng hàm động (
currentUser(),openSprints(),now()) thay vì hardcode, và để owner là group cho filter dùng chung. Bài học từ Tiki và FPT Software cho thấy filter được tổ chức tốt giúp cả team tiết kiệm thời gian và tránh "trôi" sau nhiều tháng.
- Góc nhìn BA là góc nhìn chất lượng dữ liệu. Những filter giá trị nhất của bạn thường tìm cái thiếu — story thiếu AC, issue chưa estimate, bug chưa gán, việc bị bỏ quên. Đó là chỗ bạn tạo ra khác biệt so với một người chỉ biết gõ JQL.