Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa ship một tính năng "Live Shopping" cho sàn thương mại điện tử. Một tuần sau, sếp hỏi: "Tính năng đó có ai dùng không? Tỷ lệ người xem live rồi thêm vào giỏ là bao nhiêu? Họ thoát ra ở bước nào?" Bạn quay sang đội data, và đội data nhún vai: "Bọn em không track được cái đó, trong PRD anh không nói rõ phải log sự kiện gì."
Đó chính là lý do bài này tồn tại. Một PRD có thể mô tả hoàn hảo behavior người dùng, vẽ flow đẹp, liệt kê đủ edge case — nhưng nếu thiếu phần Analytics Event Spec, thì sau khi ship bạn sẽ mù tịt. Bạn không biết tính năng có thành công không, không biết đo metric đã hứa trong phần Goals ra sao, và quan trọng nhất: bạn không có dữ liệu để quyết định v2.
Analytics Event Spec là phần đặc tả chính xác những sự kiện (event) nào sẽ được ghi nhận, đặt tên ra sao, mang theo thuộc tính (property) gì, và bắn vào thời điểm nào. Nó là cầu nối giữa "ý định đo lường" của PM và "code instrument" của engineer. Viết phần này tốt thì sau ship bạn có dashboard chuẩn ngay từ ngày đầu. Viết ẩu thì bạn sẽ có một mớ event hỗn loạn, trùng tên, sai schema, và phải làm lại từ đầu — một việc cực kỳ tốn kém vì dữ liệu đã mất là mất vĩnh viễn, không backfill được.
Trong bài này chúng ta chỉ tập trung vào đặc tả event — naming, property, timing, và governance. Việc dùng những event này để chạy A/B test hay phân tích thực nghiệm là chủ đề của bài khác; ở đây bạn học cách viết ra cái "hợp đồng dữ liệu" rõ ràng để engineer implement đúng ngay lần đầu.
Khái niệm cốt lõi
Một event gồm những gì?
Một analytics event tối thiểu có ba thành phần:
- Tên event (event name) — định danh hành động, ví dụ
add_to_cart. - Properties — các thuộc tính đính kèm mô tả ngữ cảnh, ví dụ
product_id,price,source_screen. - Timing / Trigger — thời điểm chính xác event được bắn, ví dụ "khi người dùng nhấn nút Thêm vào giỏ VÀ request server trả về thành công".
user_id, device_type, app_version, timestamp. Bạn không cần đặc tả lại những cái này, nhưng cần biết chúng tồn tại để không track trùng.Quy ước đặt tên: snake_case, Verb + Object, thì quá khứ
Đây là phần ý định gốc của bài, và nó quan trọng đến mức đáng được khắc lên tường:
snake_case. Tất cả tên event và property dùng chữ thường, nối bằng dấu gạch dưới: live_stream_started, add_to_cart_from_live, checkout_completed. Không dùng camelCase, không dùng dấu cách, không dùng tiếng Việt có dấu. Lý do: nhất quán giúp query dễ, tránh lỗi do hệ thống phân biệt hoa thường, và dễ đọc trên dashboard.
Verb + Object (Động từ + Tân ngữ). Mỗi event mô tả một hành động lên một đối tượng: started (verb) + live_stream (object) → live_stream_started. Cấu trúc này khiến event tự giải thích được mà không cần đọc tài liệu. So sánh live_stream_started (rõ) với live1 hay event_a (vô nghĩa) — bạn thấy ngay sự khác biệt.
Thì quá khứ (past tense). Event ghi nhận một việc đã xảy ra, nên động từ chia ở thì quá khứ: started, completed, viewed, clicked, added, removed. Không dùng start_live_stream (mệnh lệnh) hay starting (đang diễn ra). Quy ước này nhỏ nhưng giúp toàn bộ taxonomy thống nhất về mặt ngữ nghĩa: dashboard của bạn sẽ đọc như một dòng nhật ký các việc đã hoàn tất.
Object-Action vs Action-Object
Có hai trường phái sắp xếp thứ tự. Trường phái Action-Object (viewed_product, started_live_stream) đọc tự nhiên như tiếng Anh. Trường phái Object-Action (product_viewed, live_stream_started) gom nhóm tốt hơn trên dashboard vì mọi event của cùng một object nằm cạnh nhau khi sắp xếp theo alphabet — tất cả live_stream_* đứng cùng chỗ. Cả hai đều ổn; điều bắt buộc là chọn MỘT và áp dụng nhất quán toàn dự án. Amplitude và nhiều team lớn nghiêng về Object-Action vì lợi ích gom nhóm. Trong bài này tôi dùng Object-Action.
Property cũng cần quy ước
Property cũng dùng snake_case. Đặt tên mô tả rõ giá trị: source_screen thay vì from, payment_method thay vì pm. Property nên nhất quán giữa các event: nếu product_id xuất hiện ở event này, thì ở event khác cũng phải gọi là product_id chứ không phải productId hay item_id. Một property tên thống nhất trên toàn hệ thống cho phép bạn join và phân tích chéo dễ dàng.
Phân biệt event với property
Một sai lầm kinh điển là biến giá trị thành tên event. Đừng tạo clicked_buy_button_red và clicked_buy_button_blue. Hãy tạo MỘT event buy_button_clicked với property button_color: "red" | "blue". Nguyên tắc: hành động là event, biến thể của hành động là property. Nếu không, taxonomy của bạn sẽ phình ra hàng nghìn event vô dụng và không thể phân tích nổi.
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT Việt Nam và tính năng Live Shopping
Một sàn thương mại điện tử lớn tại Việt Nam (giả định tên "ChợViet") ra mắt Live Shopping cho phép shop livestream và người xem mua hàng ngay trong live. PM viết Goals: "Đạt tỷ lệ chuyển đổi từ xem live sang đặt hàng (live-to-order) ≥ 4% trong quý đầu."
Phiên bản đầu của Analytics Spec do một junior PM viết khá lộn xộn: LiveStart, user_watch, clickBuyNow, order. Đội data implement xong, ba tuần sau muốn tính live-to-order conversion thì phát hiện không có cách nối phiên live với đơn hàng — event order không mang live_session_id, nên không biết đơn nào đến từ live nào.
Phiên bản viết lại đúng chuẩn:
| Event | Khi nào bắn | Properties chính |
|---|---|---|
live_stream_started | Shop bắt đầu phát | live_session_id, shop_id, scheduled (bool) |
live_stream_viewed | Người xem vào live, sau 3 giây ổn định | live_session_id, shop_id, entry_source |
add_to_cart_from_live | Thêm giỏ thành công từ trong live | live_session_id, product_id, price, quantity |
checkout_completed | Server xác nhận đặt hàng thành công | order_id, live_session_id (nullable), total_amount, payment_method |
live_session_id xuyên suốt từ lúc xem đến lúc checkout mà PM tính được live-to-order conversion bằng một query đơn giản. Họ đo ra 3.1% ở tháng đầu, phát hiện đa số rớt ở bước add-to-cart vì nút bị che bởi bình luận, sửa UI và lên 4.8% ở tháng ba. Không có spec event chuẩn, toàn bộ chuỗi phân tích này là bất khả thi.Ví dụ 2 — Grab và bài học timing của event
Một bài học timing kinh điển: với app gọi xe như Grab, đặc tả "khi nào bắn event" quan trọng ngang với tên event. Giả sử team đặc tả ride_requested bắn ngay khi người dùng nhấn nút "Tìm tài xế". Nghe hợp lý. Nhưng nếu request đó thất bại do mất mạng hoặc server lỗi, event vẫn bắn — và dashboard sẽ thổi phồng số lượng chuyến được yêu cầu, làm sai lệch tỷ lệ match tài xế.
Cách đặc tả đúng phải phân tách rõ:
ride_request_tapped— bắn ngay khi nhấn nút (đo ý định người dùng).ride_requested— chỉ bắn khi server nhận và xác nhận yêu cầu hợp lệ (đo yêu cầu thực).ride_request_failed— bắn khi request lỗi, kèm propertyfailure_reason: "network" | "no_driver" | "server_error".
Ví dụ 3 — Startup fintech và thảm họa "schema trôi dạt"
Một startup ví điện tử (giả định "MoMoney") để mỗi đội tự đặt event không có governance. Sau một năm, họ có 2.300 event, trong đó transaction_success, txn_completed, payment_done, và transfer_ok đều mô tả... cùng một việc, do bốn đội khác nhau tạo. Khi CEO hỏi "tổng số giao dịch thành công tháng này", không ai trả lời nổi con số đúng vì phải cộng dồn nhiều event với schema khác nhau, và một số event thiếu amount.
Họ phải dừng lại làm một đợt "tracking plan migration" kéo dài hai tháng: lập một bảng tracking plan tập trung (dùng Google Sheet rồi chuyển sang Avo), gộp về một event chuẩn transaction_completed với property bắt buộc amount, currency, transaction_type, status. Bài học: governance phải có từ đầu, không phải vá sau. Một tracking plan tập trung — nơi mọi event mới phải đăng ký và review trước khi code — rẻ hơn rất nhiều so với việc dọn dẹp 2.300 event hỗn loạn.
Hướng dẫn từng bước
Đây là quy trình viết Analytics Event Spec trong PRD của bạn:
Bước 1 — Bắt đầu từ câu hỏi, không phải từ event. Liệt kê những câu hỏi bạn sẽ cần trả lời sau khi ship: "Bao nhiêu người dùng tính năng?", "Họ rớt ở bước nào?", "Metric trong Goals đo bằng gì?". Mỗi câu hỏi sẽ chỉ ra event cần track. Cách này tránh việc track thừa (track mọi click vô tội vạ) lẫn track thiếu.
Bước 2 — Vẽ user flow và đánh dấu các điểm chạm. Lấy flow chính của tính năng, đặt một dấu chấm tại mỗi bước quan trọng người dùng đi qua. Mỗi dấu chấm thường tương ứng một event. Funnel của bạn (xem → thêm giỏ → checkout) chính là chuỗi event.
Bước 3 — Đặt tên theo quy ước. Áp dụng snake_case + Object-Action (hoặc Action-Object) + thì quá khứ. Ví dụ: live_stream_viewed, cart_item_added, checkout_completed. Giữ nhất quán tuyệt đối.
Bước 4 — Định nghĩa properties cho từng event. Với mỗi event, ghi rõ: tên property, kiểu dữ liệu (string, int, boolean, enum), bắt buộc hay tùy chọn, và ví dụ giá trị. Đặc biệt chú ý các property "khóa nối" như session_id, product_id để phân tích chéo về sau.
Bước 5 — Đặc tả timing và nơi bắn. Với mỗi event ghi rõ: client hay server, trước hay sau khi có phản hồi, điều kiện kích hoạt cụ thể. Đây là phần engineer cần nhất.
Bước 6 — Lập bảng tracking plan trong PRD. Trình bày tất cả dưới dạng bảng để engineer và data đọc một lần là hiểu. Một dòng = một event. Cột: Event name | Trigger | Properties | Type & required | Ghi chú.
Bước 7 — Review chéo với Data/Engineer trước khi code. Cho đội data xác nhận họ có thể trả lời được các câu hỏi ở Bước 1 với taxonomy này. Đây là điểm "đông cứng" hợp đồng dữ liệu — sửa lúc này miễn phí, sửa sau khi ship rất đắt.
Một dòng tracking plan mẫu hoàn chỉnh:
Event: add_to_cart_from_live
Trigger: Server xác nhận thêm sản phẩm vào giỏ thành công, từ màn hình live
Properties:
- live_session_id (string, required) ví dụ "ls_8842"
- product_id (string, required) ví dụ "p_10293"
- price (int, required) đơn vị VND, ví dụ 199000
- quantity (int, required) ví dụ 1
- source (enum, required) "live" | "live_replay"
Fired on: Client, sau khi nhận response 200 từ API addToCart
Lỗi thường gặp & mẹo
Lỗi 1 — Biến giá trị thành tên event. clicked_red_button, clicked_blue_button. Sửa: một event button_clicked + property color.
Lỗi 2 — Bắn event ở client trước khi xác nhận thành công. Khiến số liệu thổi phồng. Mẹo: event đo "thành công" nên bắn ở server hoặc sau response OK; tách riêng event đo "ý định" nếu cần cả hai.
Lỗi 3 — Quên property khóa nối. Không có session_id / order_id thì không thể dựng funnel hay nối các bước. Mẹo: với mọi luồng nhiều bước, luôn truyền một id xuyên suốt.
Lỗi 4 — Đặt tên không nhất quán. productId ở event này, product_id ở event kia. Mẹo: lập một "từ điển property" dùng chung cho cả dự án.
Lỗi 5 — Track quá nhiều ("track mọi thứ cho chắc"). Dẫn đến noise, tốn tiền tool, và không ai phân tích nổi. Mẹo: chỉ track event trả lời được một câu hỏi cụ thể. Nếu không nghĩ ra câu hỏi, đừng track.
Lỗi 6 — Gửi dữ liệu nhạy cảm (PII) vào property. Không bao giờ để số điện thoại, email, số thẻ vào property — vừa vi phạm quyền riêng tư vừa rủi ro pháp lý. Mẹo: chỉ gửi id đã ẩn danh.
Lỗi 7 — Không version hóa schema. Khi cần đổi cấu trúc event, đừng âm thầm đổi nghĩa property cũ. Mẹo: thêm property mới hoặc tạo event mới, giữ event cũ để dữ liệu lịch sử không gãy.
Mẹo vàng: Luôn coi tên event và schema property là một hợp đồng không thể đổi ngược (immutable contract). Một khi đã ship và bắt đầu thu dữ liệu, đổi tên hay đổi nghĩa sẽ làm gãy dashboard và mất khả năng so sánh lịch sử. Hãy thiết kế cẩn thận từ đầu.
Bài tập thực hành
Đề bài: Bạn là PM cho một app giao đồ ăn (kiểu ShopeeFood / GrabFood). Tính năng mới: "Đặt nhóm" (group order) cho phép nhiều người cùng thêm món vào một đơn chung rồi một người thanh toán.
Yêu cầu:
- Liệt kê ít nhất 4 câu hỏi phân tích bạn muốn trả lời sau khi ship (ví dụ: bao nhiêu đơn nhóm được tạo nhưng không hoàn tất?).
- Thiết kế tối thiểu 5 event theo đúng quy ước snake_case + Object-Action + thì quá khứ. Gợi ý các mốc: tạo nhóm, mời thành viên, thành viên tham gia, thêm món, thanh toán.
- Với mỗi event, ghi rõ properties (tên, kiểu, required/optional) và timing (client/server, trước/sau response). Nhớ thêm một property khóa nối xuyên suốt để dựng funnel.
- Chỉ ra một cặp event dễ bị đặt sai thành "value-as-event" và viết lại đúng (event + property).
group_order_id truyền qua mọi bước. Các event có thể là: group_order_created, group_order_member_invited, group_order_member_joined, group_order_item_added, group_order_checkout_completed. Câu hỏi "tỷ lệ nhóm tạo xong nhưng không thanh toán" tính được nhờ so sánh số group_order_created với số group_order_checkout_completed có cùng group_order_id.Tóm tắt
- Analytics Event Spec là phần PRD đặc tả chính xác event nào được track, tên gì, mang property gì, bắn khi nào — là hợp đồng dữ liệu giữa PM và engineer.
- Quy ước đặt tên cốt lõi: snake_case, Verb + Object (chọn nhất quán Object-Action hoặc Action-Object), thì quá khứ (
live_stream_started,add_to_cart_from_live). - Hành động là event, biến thể của hành động là property — đừng nhồi giá trị vào tên event.
- Timing quan trọng ngang tên: ghi rõ client/server và trước/sau response; tách event đo ý định khỏi event đo thành công.
- Luôn có property khóa nối (session_id, order_id) để dựng funnel và phân tích chéo.
- Governance từ đầu: một tracking plan tập trung, review trước khi code, rẻ hơn nhiều so với dọn dẹp taxonomy hỗn loạn sau này.
- Coi schema là immutable contract: thiết kế kỹ vì dữ liệu đã mất không backfill được, và đổi tên làm gãy lịch sử.
- Quy trình 7 bước: bắt đầu từ câu hỏi → đánh dấu flow → đặt tên → định nghĩa property → đặc tả timing → lập bảng tracking plan → review chéo với Data/Engineer.