Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Spec cho mobile feature (deeplink, push, offline)

Viết PRD & Product Spec cho PM Bài 6/60

Mở đầu — vì sao bài này quan trọng

Bạn vừa học cách viết Problem, Goals, user stories và acceptance criteria. Đó là bộ khung chung cho mọi PRD. Nhưng khi bạn viết spec cho một tính năng mobile, có ba "vùng đất" mà người mới viết spec gần như luôn bỏ sót — và đó cũng chính là ba thứ làm engineer phải quay lại hỏi bạn liên tục, làm QA tìm ra bug ngay tuần đầu, và làm tính năng của bạn ship trễ một sprint: deeplink, push notification, và hành vi offline.

Vì sao mobile lại khác? Trên web, người dùng gõ URL, server trả về trang, mọi thứ tương đối tuyến tính. Trên mobile, app sống trong một thế giới đầy gián đoạn: người dùng nhận một thông báo lúc đang đứng xếp hàng, bấm vào, app phải mở đúng màn hình; họ đặt đơn hàng trong thang máy mất sóng, thao tác phải được "giữ" lại rồi gửi đi khi có mạng; họ click một link Grab gửi qua Zalo, app phải biết mở chi tiết chuyến xe chứ không phải màn hình chủ. Mỗi tình huống này là một nhánh hành vi mà nếu spec không mô tả rõ, engineer sẽ tự đoán — và họ thường đoán sai.

Bài học này dạy bạn cách spec đúng ba thứ đặc thù của mobile đó. Đây không phải lý thuyết: đây là phần spec mà nếu bạn viết tốt, đội kỹ thuật sẽ thầm cảm ơn bạn; nếu viết sót, bạn sẽ trả giá bằng những cuộc họp khẩn ngày ship.

Khái niệm cốt lõi

Một tính năng mobile, ngoài phần "màn hình và logic" giống web, còn cần spec rõ ba lớp đặc thù. Hãy nắm bản chất từng lớp trước.

Deeplink — đường dẫn vào sâu bên trong app

Deeplink là một URL hoặc đường dẫn dẫn người dùng vào thẳng một màn hình cụ thể bên trong app, thay vì chỉ mở app ở màn hình chủ. Có ba loại bạn cần phân biệt:

  • Custom scheme deeplink: dạng grabapp://ride/booking/12345. Đơn giản, nhanh, nhưng nếu app chưa cài thì link chết.
  • Universal Links (iOS) / App Links (Android): dạng https://grab.com/ride/12345. Cùng một link https: nếu có app thì mở app, nếu không thì mở web. Đây là chuẩn nên dùng cho link gửi ra ngoài (SMS, email, Zalo).
  • Deferred deeplink: dành cho người chưa cài app. Họ bấm link, được đưa đến store, cài app, mở lần đầu và app vẫn "nhớ" đưa họ tới đúng nội dung ban đầu. Cực kỳ quan trọng cho marketing và referral.
Khi spec deeplink, bạn cần định nghĩa một URL schema rõ ràng: cấu trúc đường dẫn, tham số, và — quan trọng nhất — hành vi cho mọi trạng thái: app đã đăng nhập chưa, nội dung còn tồn tại không, người dùng có quyền xem không, app đã cài chưa.

Push notification — kéo người dùng quay lại

Push notification có hai phần spec mà PM phải chịu trách nhiệm:

  • Copy (nội dung): title, body, và đôi khi hình ảnh. Đây là phần PM viết, không phải engineer.
  • Targeting & trigger: ai nhận, khi nào gửi, điều kiện nào kích hoạt, tần suất tối đa, và — đừng quên — deeplink đích khi người dùng bấm vào.
Một điểm tinh tế: push có hai loại — transactional (giao dịch: "Đơn hàng của bạn đã giao") luôn gửi, và marketing/engagement ("Giảm 50% hôm nay!") phải tôn trọng quyền opt-out và giới hạn tần suất. Spec phải nói rõ loại nào.

Offline behavior — app phải sống được khi mất mạng

Đây là phần khó nhất và bị bỏ sót nhiều nhất. Câu hỏi cốt lõi: khi không có mạng, thao tác của người dùng sẽ ra sao? Có ba chiến lược chính:

  • Read-only cache: hiển thị dữ liệu đã tải trước đó, không cho thao tác ghi.
  • Optimistic UI + queue: cho người dùng thao tác (like, gửi tin nhắn, thêm vào giỏ), giao diện cập nhật ngay như đã thành công, còn thao tác được xếp hàng (queue) và gửi lên server khi có mạng trở lại.
  • Block với thông báo: chặn thao tác, hiện thông báo "Cần kết nối mạng".
Với chiến lược queue, bạn phải spec rõ: queue lưu ở đâu, retry mấy lần, xử lý xung đột (conflict) thế nào khi dữ liệu trên server đã thay đổi, và điều gì xảy ra nếu queue thất bại vĩnh viễn.

Tình huống thực tế

Ví dụ 1 — ShopeeFood và deeplink từ chiến dịch khuyến mãi

Một đội growth của một app giao đồ ăn (lấy bối cảnh tương tự ShopeeFood tại Việt Nam) chạy chiến dịch gửi SMS: "Giảm 40k cho đơn đầu tiên, bấm vào đây". Link trong SMS là https://order.app/promo/FIRST40.

PM ban đầu chỉ spec: "Link mở app vào trang nhập mã FIRST40". Nghe đủ. Nhưng khi launch, support nhận hàng trăm phàn nàn. Vì sao? Phần lớn người nhận SMS là người chưa cài app (đó là mục tiêu của chiến dịch acquisition). Họ bấm link, được đưa tới CH Play, cài app, mở lên — và rơi vào màn hình đăng nhập trống trơn. Mã FIRST40 biến mất. Tỷ lệ áp dụng mã chỉ đạt 8%.

Đội phải làm lại spec, bổ sung phần deferred deeplink: khi người dùng cài app từ link này, app phải lưu lại tham số promo=FIRST40 (qua một dịch vụ attribution như AppsFlyer hoặc Branch), và sau khi đăng nhập lần đầu, tự động áp mã vào giỏ. Họ cũng thêm trạng thái: nếu mã đã hết hạn, hiện thông báo rõ ràng thay vì màn hình trống. Sau khi sửa, tỷ lệ áp dụng mã tăng lên 31%.

Bài học: Khi spec deeplink cho marketing, luôn hỏi "người này đã cài app chưa?". Nếu chiến dịch nhắm người mới, bạn bắt buộc phải spec deferred deeplink — nếu không bạn đang đổ tiền quảng cáo vào một cái phễu thủng đáy.

Ví dụ 2 — Một app ngân hàng số và push notification gây hoảng loạn

Một ngân hàng số tại Đông Nam Á tung tính năng thông báo biến động số dư qua push. Engineer triển khai theo spec: "Khi tài khoản có giao dịch, gửi push thông báo số dư mới".

Sự cố xảy ra vào một sáng thứ Hai. Hệ thống đối soát chạy batch, tạo ra hàng loạt bút toán nội bộ. Mỗi bút toán kích hoạt một push. Một số khách hàng nhận hơn 40 thông báo trong vài phút, nội dung kiểu "Số dư của bạn thay đổi". Họ hoảng, tưởng tài khoản bị hack, gọi tổng đài dồn dập, social media bùng lên.

Vấn đề nằm ở spec thiếu ba thứ: (1) điều kiện trigger quá rộng — không phân biệt giao dịch thực của khách với bút toán nội bộ; (2) không có rate limiting — không giới hạn số push mỗi người mỗi khoảng thời gian; (3) copy mơ hồ — "Số dư thay đổi" thay vì "Bạn vừa nhận 500.000đ từ Nguyen Van A".

Spec làm lại định nghĩa rõ: chỉ trigger trên giao dịch loại customer_facing; gộp (debounce) nhiều giao dịch trong 60 giây thành một thông báo tổng hợp; tối đa 1 push mỗi 30 giây cho cùng một người với loại số dư; và copy cụ thể có số tiền, bên đối tác, thời gian.

Bài học: Push spec không chỉ là viết câu chữ. Phần nguy hiểm nhất là trigger condition và rate limiting. Luôn tự hỏi: "Trong trường hợp xấu nhất, một người dùng có thể nhận bao nhiêu push từ tính năng này trong một phút?" Nếu bạn không trả lời được, spec chưa xong.

Ví dụ 3 — Grab và hành vi offline khi đặt xe

Hình dung một tài xế công nghệ tại TP.HCM đang trong hầm gửi xe của một toà nhà, mất sóng. Anh ta bấm "Hoàn thành chuyến". Nếu app chỉ hiện "Không có mạng, thử lại", anh ta sẽ kẹt: không thể nhận chuyến mới, và khách đã xuống xe.

Spec offline tốt cho tình huống này dùng optimistic UI + queue: khi tài xế bấm "Hoàn thành", app ngay lập tức hiển thị màn hình "Đã hoàn thành", lưu hành động vào queue cục bộ kèm timestamp và GPS cuối cùng. Khi ra khỏi hầm có sóng, app tự gửi sự kiện lên server. Server xử lý conflict: nếu timestamp hợp lệ và chuyến chưa bị hệ thống tự đóng, chấp nhận; nếu không, hiện thông báo và yêu cầu xác nhận lại.

Spec này phải mô tả rõ: queue giữ tối đa bao nhiêu hành động, retry với backoff (thử lại sau 2s, 4s, 8s...), điều gì hiển thị nếu sau 10 phút vẫn không gửi được, và làm sao tránh double submit (gửi hai lần khi mạng chập chờn) bằng một idempotency key duy nhất cho mỗi hành động.

Bài học: Offline không phải tính năng "nice to have" cho app vận hành thực tế ở Việt Nam — nơi sóng yếu trong hầm, thang máy, vùng ven là chuyện thường ngày. Một spec offline tốt biến trải nghiệm bực bội thành liền mạch. Và idempotency là từ khoá bạn phải nhớ: nó ngăn người dùng vô tình tạo hai đơn hàng, hai giao dịch.

Hướng dẫn từng bước

Khi viết phần mobile-specific cho spec, làm theo trình tự sau.

Bước 1 — Liệt kê mọi điểm vào (entry points) bằng deeplink. Với mỗi màn hình quan trọng, hỏi: có ai cần link thẳng vào đây không? Từ push? Từ SMS? Từ chia sẻ của người dùng? Lập một bảng schema:

ĐíchDeeplinkTham sốTrạng thái cần xử lý
Chi tiết đơnapp.com/order/{id}idChưa login / đơn không tồn tại / không có quyền / chưa cài app
Bước 2 — Định nghĩa hành vi cho từng trạng thái của deeplink. Đừng chỉ viết "happy path". Viết rõ: chưa đăng nhập thì sao (lưu link, đăng nhập xong quay lại), nội dung đã bị xoá thì sao (màn hình thông báo lịch sự, không crash), không có quyền thì sao, app chưa cài thì sao (deferred deeplink hoặc fallback web).

Bước 3 — Spec từng loại push notification. Với mỗi push, ghi: loại (transactional/marketing), điều kiện trigger chính xác, đối tượng nhận (targeting), tần suất tối đa, copy đầy đủ (title + body, có ví dụ với dữ liệu thật), và deeplink đích khi bấm. Viết luôn cả hành vi khi người dùng đã tắt thông báo.

Bước 4 — Chọn chiến lược offline cho từng thao tác. Phân loại mỗi thao tác của người dùng vào: read-only cache, optimistic + queue, hay block. Với thao tác queue, spec rõ: lưu trữ, retry policy, conflict resolution, idempotency key, và giới hạn queue.

Bước 5 — Viết acceptance criteria cho cả ba lớp. Mỗi trạng thái deeplink, mỗi điều kiện push, mỗi nhánh offline đều cần một tiêu chí kiểm thử rõ ràng để QA có thể verify. (Bạn đã học cách viết acceptance criteria ở bài trước — áp dụng vào đây.)

Lỗi thường gặp & mẹo

Lỗi 1 — Chỉ spec happy path của deeplink. Người mới chỉ viết "link mở chi tiết sản phẩm". Họ quên: sản phẩm hết hàng, đã bị gỡ, người dùng chưa login, hoặc chưa cài app. Mẹo: với mỗi deeplink, luôn duyệt checklist bốn câu hỏi — đã login? nội dung còn không? có quyền không? app đã cài chưa?

Lỗi 2 — Để engineer tự viết copy push. Kết quả là những thông báo cụt lủn, sai chính tả tiếng Việt, không có dấu, hoặc lộ thông tin nhạy cảm trên màn hình khoá. Mẹo: PM viết copy đầy đủ, có ví dụ với dữ liệu thật, và ghi rõ nội dung nào không được hiện trên lock screen (số tiền lớn, mã OTP, thông tin sức khoẻ).

Lỗi 3 — Quên rate limiting và debounce cho push. Đây là lỗi gây khủng hoảng PR như ví dụ ngân hàng ở trên. Mẹo: luôn ghi một dòng "Tối đa X push / người / khoảng thời gian Y" cho mỗi loại thông báo.

Lỗi 4 — Mặc định "có mạng" cho mọi thao tác. Spec viết như thể người dùng luôn online. Mẹo: đọc lại spec và với mỗi nút bấm gửi dữ liệu, tự hỏi "nếu lúc này mất mạng thì sao?".

Lỗi 5 — Quên idempotency. Mạng chập chờn khiến app gửi hai lần, tạo hai đơn hàng. Mẹo: mọi thao tác ghi đi qua queue phải có một khoá duy nhất để server nhận diện và loại trùng.

Mẹo tổng: Dùng một bảng "trạng thái x hành vi" cho cả ba lớp. Bảng buộc bạn phải nghĩ tới mọi ô — đó chính là chỗ engineer hay hỏi và QA hay tìm thấy bug.

Bài tập thực hành

Chọn một tính năng mobile cụ thể (ví dụ: "Chia sẻ một bài viết yêu thích cho bạn bè" trong một app đọc tin tức Việt Nam). Viết phần mobile-specific spec gồm:

  • Bảng deeplink schema cho màn hình bài viết: URL, tham số, và hành vi cho cả bốn trạng thái (chưa login / bài đã gỡ / không có quyền / chưa cài app — bao gồm cả deferred deeplink khi người được chia sẻ chưa có app).
  • Spec hai push notification: một cái transactional ("Bài viết bạn lưu đã có phần tiếp theo") và một cái marketing ("5 bài đọc hot tối nay"). Với mỗi cái, viết: trigger, targeting, tần suất tối đa, copy đầy đủ (có ví dụ dữ liệu thật), và deeplink đích.
  • Chiến lược offline cho thao tác "Lưu bài viết": chọn read-only cache / optimistic + queue / block, rồi spec retry policy và idempotency key.
Viết dưới dạng bảng và bullet rõ ràng để một engineer có thể đọc và code mà không cần hỏi lại bạn câu nào. Đó là tiêu chí thành công.

Tóm tắt

Spec cho tính năng mobile cần thêm ba lớp đặc thù mà web không có. Deeplink: định nghĩa URL schema rõ ràng và — quan trọng hơn — hành vi cho mọi trạng thái (chưa login, nội dung mất, không quyền, chưa cài app, kể cả deferred deeplink). Push notification: PM chịu trách nhiệm copy và phần targeting/trigger; đừng bao giờ quên rate limiting và debounce, vì đó là nguồn gốc của những khủng hoảng như "40 thông báo trong vài phút". Offline: chọn chiến lược (read-only / optimistic + queue / block) cho từng thao tác, và với queue thì luôn spec retry, conflict resolution và idempotency key để tránh double submit.

Mẫu số chung: với mỗi lớp, lập một bảng "trạng thái x hành vi". Bảng đó buộc bạn nghĩ tới mọi ô — chính là những chỗ engineer hay hỏi và QA hay tìm thấy bug. Viết tốt ba lớp này, spec mobile của bạn sẽ ship đúng hạn và đội kỹ thuật sẽ thầm cảm ơn bạn.