Building Relay

Phần 0 · Chương 0.2

Bốn người sẽ phán xét chúng ta

Bạn sẽ tạo ra: Một bộ chân dung người dùng, bao gồm cả người dùng cuối vô hình · khoảng 75 phút, bao gồm bài tập

Tài liệu gốc: Chân dung người dùng (tiếng Anh)

Ở chương 0.1, chúng ta đã nén cả sản phẩm vào một câu duy nhất, và câu ấy gọi tên một khách hàng: các đội kỹ sư sản phẩm tại những công ty phần mềm B2B và marketplace. Nghe rất ổn trên một trang pitch deck. Nhưng lại vô dụng trước một quyết định thiết kế. Một "đội ngũ" đâu có ngồi đọc tài liệu của chúng ta lúc mười một giờ đêm, đâu có quyền phủ quyết hợp đồng, đâu có mở lại một cuộc hội thoại để phân xử tranh chấp, và càng không đóng sập ứng dụng trong hầm gửi xe vì một tin nhắn đến hai lần. Chỉ có những con người cụ thể mới làm những việc ấy. Trước khi viết bất kỳ dòng yêu cầu nào, chúng ta cần biết họ là ai.

Vậy nên chương này biến bản tuyên ngôn định vị thành những con người — bốn người — và quan trọng hơn, phơi bày trọn vẹn quá trình suy ra họ, bởi chính cái quá trình ấy là thứ bạn sẽ lặp lại cho sản phẩm của mình ở cuối chương.

Một trật tự lẽ ra phải khiến bạn khựng lại

Các chân dung của chúng ta được xếp theo mức độ ảnh hưởng lên sản phẩm, và với một nền tảng dành cho lập trình viên, trật tự ấy vừa khác thường vừa hoàn toàn có chủ đích: người dùng sản phẩm, người trả tiền cho nó, và người hưởng lợi từ nó là ba con người khác nhau — và người hưởng lợi thì không bao giờ biết nó tồn tại.

Hãy đọc lại câu đó một lần nữa, bởi nó là sự thật kỳ lạ nhất về các sản phẩm hạ tầng, đồng thời là cội nguồn của phần lớn sai lầm thiết kế trong địa hạt này. Một ứng dụng tiêu dùng chỉ cần làm hài lòng một người. Còn chúng ta có cả một chuỗi: một lập trình viên chọn chúng ta, một giám đốc phê duyệt chúng ta, một trưởng nhóm support vận hành thứ mà lập trình viên kia xây trên nền chúng ta, và một bác tài giao hàng — người sẽ không bao giờ nghe đến cái tên Relay — hứng trọn từng cú trễ latency mà chúng ta để lọt. Mỗi người trong số họ có thể khai tử sản phẩm theo một cách riêng. Hãy lần lượt gặp họ, theo đúng trật tự ảnh hưởng.

Mai — người mà sản phẩm này sinh ra để phục vụ

Mai là một kỹ sư full-stack thâm niên, hai mươi chín tuổi, làm việc tại một nền tảng điều phối logistics khoảng ba mươi kỹ sư ở Thành phố Hồ Chí Minh. Cô làm cả React lẫn Node, ôm trọn tính năng từ đầu đến cuối, và đã từng ship các tích hợp với Stripe, Twilio cùng hai nhà cung cấp bản đồ. Cô không phải chuyên gia hạ tầng và — điều này rất quan trọng — cô cũng chẳng muốn trở thành một người như thế.

Tình cảnh của cô chính là tính năng "hai tuần" của chương 0.1, bằng xương bằng thịt. PM của cô đã cam kết tính năng nhắn tin giữa điều phối viên và tài xế cho quý tới. Mai bỏ ra một cuối tuần dựng thử bằng ws và Postgres; nó chạy đẹp như mơ với một server. Rồi cô nghĩ đến hai server, nghĩ đến chuyện điện thoại của một bác tài rớt khỏi sóng 4G giữa cuộc hội thoại — và thế là cô quay xe, gác bản prototype lại để chuyển sang đọc tài liệu của các vendor.

Hãy quan sát cách cô đánh giá, bởi mỗi bước chân của cô là một bản yêu cầu hệ thống đang khoác thường phục. Cô đọc tài liệu trước khi đăng ký. Cô tìm một bản quickstart có thể dán thẳng vào một file nháp. Cô kiểm tra xem tài liệu giao thức WebSocket có đặc tả rõ chuyện kết nối lại và thứ tự tin nhắn hay không — với cô, chi tiết ấy là thước đo cho việc đội ngũ đứng sau sản phẩm có nghiêm túc hay không. Cô tìm mục xử lý lỗi trước cả danh sách tính năng. Rồi cô dựng một prototype dùng-xong-vứt và đem khoe một đồng nghiệp.

Điều chinh phục cô: một cuộc chat chạy được giữa hai tab trình duyệt trong vòng chưa đầy mười phút; một SDK thuần vận chuyển và trạng thái, không kèm component nào (design system của cô là thứ không thể thương lượng); một bản đặc tả OpenAPI có thể sinh types; những phản hồi lỗi gọi đích danh field gây lỗi. Điều đánh mất cô, ngay lập tức: "Hãy liên hệ với chúng tôi để được cấp quyền API", một bản quickstart chạy không đúng như đã viết, hay bất kỳ bộ UI kit bắt buộc nào. Câu nói của cô là bản tài liệu yêu cầu gọn gàng nhất mà chúng ta từng nhận được: "Tôi không cần nó làm được mọi thứ. Tôi cần biết chính xác nó làm gì và chuyện gì xảy ra khi nó hỏng."

David — người mua nắm quyền phủ quyết thứ mình không bao giờ dùng

David là giám đốc kỹ thuật, bốn mươi mốt tuổi, báo cáo trực tiếp cho CTO và quản lý bốn đội — trong đó có đội của Mai. Anh từng tự xây hệ thống nhắn tin ở công ty trước, và nhớ chính xác nó ngốn mất bao lâu — điều khiến anh có thiện cảm với phương án mua. Anh cũng là người bị dựng dậy lúc nửa đêm khi có sự cố, và là người trả lời bảng câu hỏi bảo mật của khách hàng. Anh sẽ không bao giờ đọc SDK của chúng ta. Nhưng anh vẫn có thể phủ quyết nó.

Mục tiêu của anh thuộc một thể loại khác hẳn của Mai: giữ đội ngũ tập trung vào thứ thực sự làm nên khác biệt của sản phẩm; chi phí tăng theo doanh thu một cách đoán trước được, chứ không theo những cú sốc; vượt qua đợt kiểm định bảo mật của khách hàng mà không phải chữa cháy; và tránh một sự phụ thuộc không lối thoát. Cách anh đánh giá cũng khác: anh mô phỏng bảng giá của chúng ta ở mức gấp mười lần lưu lượng hiện tại, đọc lịch sử uptime và cách chúng ta giao tiếp trong các sự cố, hỏi tin nhắn được lưu ở đâu và bị xóa như thế nào, kiểm tra xem đội của anh có rút được dữ liệu ra nếu một ngày rời đi. Và một câu hỏi không dashboard nào trả lời nổi: liệu vendor này còn tồn tại sau ba năm nữa không?

Điều chinh phục anh nghe chẳng hào nhoáng chút nào: một công cụ tính giá với những con số thật, một dashboard mức sử dụng mà đội anh có thể theo dõi hằng ngày, một lối xuất dữ liệu được ghi thành văn, ngữ nghĩa lưu trữ và xóa rõ ràng. Điều đánh mất anh: phép đo đếm mờ ám, SSO và audit log bị khóa sau gói enterprise, hay bất kỳ dấu hiệu nào cho thấy support kết thúc ở một diễn đàn cộng đồng. Câu nói của anh xứng đáng treo lên tường: "Bài toán xây-hay-mua thì dễ. Cái tôi đang tính là bài toán mua-hay-hối-hận."

Priya — người dùng nó mỗi ngày mà chẳng hề tích hợp nó

Priya dẫn dắt đội chăm sóc khách hàng (CSKH). Ba mươi tư tuổi, không thiên về kỹ thuật, nhưng hoàn toàn thoải mái với các dashboard. Đội của cô xử lý tranh chấp trên nền tảng: khi một tài xế khẳng định điều phối viên chưa từng gửi địa chỉ, cô cần xem được cuộc hội thoại; khi ai đó có hành vi lăng mạ, cô cần hành động trong vài phút — mà không phải mở ticket nhờ đội kỹ thuật chạy hộ một câu truy vấn.

Thứ cô cần đọc lên như một danh sách những gì các hệ thống chat ngây thơ vẫn xóa mất: một bản ghi chính xác, đúng thứ tự, có dấu thời gian, bao gồm cả các lần sửa và xóa; khả năng tự tay gỡ nội dung và hạn chế một người dùng; một nhật ký vết (audit trail) cho chính các thao tác kiểm duyệt của đội cô; và sự tự tin khi đứng trước một yêu cầu xóa dữ liệu. Câu nói của cô: "Tôi không cần nó đẹp. Tôi cần nó đầy đủ và đúng thứ tự."

Và đây là điểm tinh tế khiến Priya trở nên thú vị về mặt kiến trúc: Relay không phục vụ Priya một cách trực tiếp. Cô sẽ dùng một công cụ nội bộ do Mai xây. Việc của chúng ta là làm cho các API kiểm duyệt và lịch sử đủ đầy đặn, đủ trung thực để Mai dựng được công cụ ấy trong một buổi chiều. Ấy vậy mà Priya lại là lý do tồn tại của vài quyết định đắt đỏ bậc nhất của chúng ta — soft-delete bằng tombstone thay vì xóa cứng, một lịch sử chỉnh sửa bất biến, các sự kiện kiểm duyệt trong luồng webhook. Cô là bằng chứng rằng một chân dung người dùng có thể định hình cả schema mà không cần một lần chạm vào API key.

Tuan — người dùng sẽ không bao giờ nghe đến tên chúng ta

Tuan là một tài xế giao hàng, hai mươi sáu tuổi, dùng một chiếc Android tầm trung với sóng 4G chập chờn. Anh nhắn tin cho điều phối viên giữa các điểm dừng, thường bằng một tay, thường trong đường hầm hoặc hầm gửi xe. Anh không bao giờ đăng ký Relay, không bao giờ đăng nhập Relay, không bao giờ nghe đến cái tên Relay. Nhưng từng cú trễ latency đều đổ lên đầu anh.

Thứ anh cần nghe qua tưởng như hiển nhiên mà lại chẳng hề đơn giản: tin nhắn phải đến. Tin nhắn phải còn nguyên ở đó sau khi ứng dụng kết nối lại. Đúng thứ tự. Không trùng lặp khi mạng chập chờn. Không tụt pin vì một kết nối cứ retry trong vòng lặp xiết. Anh sẽ không bao giờ nói ra bất kỳ điều nào trong số này — tài liệu chân dung nói thẳng điều anh làm thay vào đó: anh chỉ đơn giản đóng ứng dụng lại nếu nó cho cảm giác thiếu tin cậy. Và khi anh đóng nó, công ty của Mai mất một tài xế, David đọc một bản báo cáo churn, và không ai mở bug ticket cho chúng ta cả.

Vì Tuan không thể cất tiếng, chúng ta phiên dịch thay anh. Mỗi nhu cầu của anh trở thành một ràng buộc cứng trên giao thức: kết nối lại kèm backfill từ một cursor, chứ không tải lại toàn bộ lịch sử. Thứ tự do server ấn định, sống sót qua những lần giao hàng sai thứ tự. Idempotency key do client cung cấp, để một lần gửi được retry không biến thành tin nhắn trùng. Exponential backoff kèm jitter khi kết nối lại. Các trạng thái sending / sent / failed tường minh do SDK phơi ra, để giao diện của Mai có thể thành thật với anh.

Khi họ va chạm nhau

Bốn con người, bốn bộ nhu cầu — va chạm là chuyện chắc chắn, và một bộ chân dung không phân xử nổi va chạm thì chỉ là đồ trang trí. Vậy nên tài liệu chân dung khép lại bằng một trật tự phân xử tường minh: độ tin cậy của Tuan thắng tất cả. Tốc độ tích hợp của Mai thắng độ rộng tính năng. Tính đoán trước được về chi phí của David thắng sự tiện lợi của Mai. Sự đầy đủ của Priya thắng hiệu quả lưu trữ.

Khoan đã. Hẳn phải có gì đó khiến bạn khựng lại — chúng ta vừa xếp các chân dung theo mức độ ảnh hưởng, và Tuan đứng chót bảng: một ràng buộc, thậm chí chẳng phải khách hàng. Thế mà trật tự phân xử lại đặt anh lên đầu tiên. Cả hai trật tự đều đúng, và nghịch lý bề ngoài ấy chính là bài học thật sự của chương này. Mức độ ảnh hưởng đo xem ai định hình các quyết định sản phẩm — Tuan chẳng định hình gì; anh sẽ không bao giờ ngồi trong buổi review roadmap của chúng ta. Còn trật tự phân xử quyết định nhu cầu của ai thắng khi các nhu cầu va nhau — và ở đó Tuan thắng tuyệt đối, chính bởi vì anh không có tiếng nói. Mai có thể mở ticket hỗ trợ, David có thể escalate, Priya có thể phàn nàn với account manager. Tuan chỉ có thể rời đi. Vậy nên chúng ta nâng nhu cầu của anh từ những lời thỉnh cầu lên thành các ràng buộc không thể thương lượng — sàn nhà của hệ thống, chứ không phải danh sách tính năng — để bảo đảm rằng sự im lặng của anh không bao giờ đồng nghĩa với thua thiệt của anh. Người trầm lặng nhất bàn tiệc nhận được bản hợp đồng vững chắc nhất.

Tài liệu còn cho trật tự này cơ hội chứng tỏ giá trị trên một va chạm thật: mã hóa đầu-cuối (E2E) sẽ phục vụ quyền riêng tư của Tuan và làm đẹp lòng đội kiểm định bảo mật của David — nhưng đồng thời khiến việc phân xử tranh chấp của Priya trở thành bất khả thi và làm phần tích hợp của Mai phức tạp lên đáng kể. Chiếu theo trật tự phân xử và thị trường mũi nêm, nó tiếp tục là một non-goal. Nếu nước đi này nghe quen tai, thì đúng là vậy: đó chính là bài học của chương 0.1 khoác lên bộ áo chân dung người dùng — lý do được ghi lại, chứ không được mặc định, để quyết định có thể được đem ra tái thẩm một cách sòng phẳng khi các dữ kiện đầu vào thay đổi.

Đến lượt bạn

Giờ hãy suy ra những con người của riêng bạn — từ bản tuyên ngôn định vị bạn đã viết ở chương 0.1, chứ không phải từ trí tưởng tượng. Nếu bạn đang mượn ý tưởng thực hành của chúng ta (công cụ đặt lịch cho phòng khám thú y đang bổ sung tính năng nhắc hẹn và cổng thông tin khách hàng), cứ tiếp tục dùng nó.

Bài tập 1 — suy ra dàn nhân vật. Đọc bản tuyên ngôn định vị của bạn và rút ra các ứng viên: ai tích hợp sản phẩm của bạn, ai trả tiền cho nó, ai vận hành thứ được xây trên nó mỗi ngày, và ai chỉ đơn thuần trải nghiệm kết quả. Rồi viết ít nhất ba chân dung theo đúng bộ trường mà tài liệu của chúng ta dùng — với mỗi người:

Sau đó xếp các chân dung theo mức độ ảnh hưởng, và viết một lý do rõ ràng cho từng bậc — "cô ấy là người duy nhất có thể tự chọn dùng chúng ta" là một lý do; thâm niên trên sơ đồ tổ chức thì không.

Bài tập 2 — phép thử vô hình. Tìm chân dung không bao giờ chọn sản phẩm của bạn: không bao giờ đăng ký, không bao giờ thấy tên bạn, nhưng hứng trọn mọi khiếm khuyết. (Phiên bản của phòng khám thú y là người chủ thú cưng chỉ mong lời nhắc hẹn đến trước buổi khám, chứ không phải sau.) Viết ra những ràng buộc họ áp lên hệ thống của bạn, theo đúng cách nhu cầu của Tuan trở thành yêu cầu giao thức — mỗi ràng buộc là một thuộc tính hệ thống buộc phải có, không phải một tính năng họ sẽ ngỏ lời xin.

Các câu tự kiểm. Mỗi câu trả lời được bằng có hoặc không:

  1. Trật tự ảnh hưởng của bạn có nêu lý do cho từng bậc, hay chỉ là thâm niên sơ đồ tổ chức trá hình?
  2. Mỗi chân dung có suy ra được từ bản tuyên ngôn định vị — một người lạ có thể lần ngược từng chân dung về một cụm từ trong đó?
  3. Có ít nhất một chân dung không bao giờ đăng ký dùng sản phẩm của bạn?
  4. Mỗi "điều đánh mất họ" có phải một hành động mà đội của bạn hoàn toàn có thể vô tình phạm phải?

Nếu câu 2 trượt, bạn vừa sáng tác ra một chân dung mà sản phẩm của bạn không hề phục vụ. Nếu câu 3 trượt, hãy nhìn kỹ hơn — các sản phẩm hạ tầng và B2B gần như luôn có một người hưởng lợi vô hình, và bỏ sót họ lúc này đồng nghĩa với việc khám phá ra các ràng buộc của họ ngay trên production.

Hãy lưu bộ chân dung cạnh các tài liệu 0.1 của bạn, trong repository. Chương 0.3 sẽ biến những con người này thành các hành trình — tấm bản đồ chỉ ra nơi ta có thể đánh mất từng người trong số họ.

Những điều đọng lại

Nếu bạn không đọc gì khác trong chương này, hãy giữ lấy những điều sau: