Bảng chấm điểm của mình chỉ để hợp thức hoá phương án đã chọn sẵn
Mỗi task, mỗi POC đều đứng trước vài phương án. Mình từng nghĩ lập bảng chấm điểm là làm việc khách quan, cho tới lúc nhận ra mình toàn chỉnh trọng số cho ra đáp án mình thích từ đầu.
Mở bài
Việc của người làm giải pháp, bóc hết mấy lớp chức danh ra, thật ra chỉ còn đúng một thứ: đứng trước hai ba phương án và chọn lấy một, rồi sống với hậu quả của nó.
Hồi mới làm thì mình chọn theo cảm giác. Đọc được bài nào hay, nghe được ông anh nào khen, thấy thư viện nào nhiều sao trên GitHub, thế là chọn. Lý do thì viết sau, và viết bao giờ cũng rất trôi chảy, vì bịa lý do cho một kết luận có sẵn là việc dễ nhất trên đời.
Sau này chững hơn một chút thì mình học được cách lập bảng. Liệt kê tiêu chí, gán trọng số, chấm điểm từng phương án, nhân lên cộng lại, ra một con số. Nhìn rất ra dáng kiến trúc sư. Đem đi họp thì cũng ít ai cãi, vì cãi với một cái bảng thì khó hơn cãi với một người.
Rồi có một hôm mình ngồi làm bảng cho một quyết định khá to, chấm xong thì phương án thắng lại không phải phương án mình nghĩ trong đầu. Phản xạ đầu tiên của mình là gì? Là ngồi sửa lại trọng số.
Đúng lúc tay đang gõ thì mình khựng lại. Nếu mình sẵn sàng sửa trọng số cho tới khi ra đúng đáp án mình muốn, thì cái bảng đó tồn tại để làm gì. Nó không giúp mình quyết định. Nó chỉ giúp mình có cái đem đi trình bày.
Bài này là mấy phương pháp chấm điểm mình đã dùng thật, cái nào hợp với loại quyết định nào, và quan trọng hơn là mấy chỗ mà chúng lừa được chính người đang cầm bút chấm.
Thân bài
1. Câu hỏi trước mọi bảng điểm: quyết định này có đáng chấm không
Cái sai phổ biến nhất mình thấy, và bản thân mình cũng dính nhiều lần, không phải là chấm điểm sai. Là chấm điểm nhầm chỗ.
Có những quyết định mà chọn sai thì tuần sau đổi lại, tốn nửa ngày. Chọn thư viện format ngày tháng, chọn tên biến trong một module nội bộ, chọn thư viện vẽ biểu đồ cho trang admin. Mấy thứ này mà đem ra cân đo ba buổi họp thì chi phí họp đã đắt hơn chi phí làm sai rồi.
Nhưng cũng có những quyết định mà sai là phải sống chung nhiều năm. Cách mình định danh người dùng và thiết bị. Kiểu dữ liệu để lưu tiền. Ranh giới giữa các service. Chọn database chính. Mấy cái này đổi được không? Được, về mặt kỹ thuật thì cái gì cũng đổi được. Nhưng phải migrate dữ liệu đang chạy, phải sửa hết chỗ gọi, phải làm khi hệ thống đang có người dùng thật ở trên đó.
Nên câu hỏi đầu tiên luôn phải là: nếu chọn sai, quay đầu tốn bao nhiêu?
Rẻ thì đừng cân đo, chọn đại một cái rồi làm, sai thì sửa, chính việc làm sẽ dạy mình nhiều hơn việc bàn. Đắt thì mới bỏ công ra chấm cho tử tế.
Chi phí ra quyết định phải tương xứng với chi phí sai, chứ không phải với độ hấp dẫn của việc ngồi tranh luận.
Cái bẫy ở đây là hai chiều, và chiều nào cũng đau. Chiều thứ nhất là họp ba buổi cho một quyết định nửa ngày là đổi được. Chiều thứ hai, âm thầm hơn và tốn kém hơn nhiều, là quyết trong ba mươi giây cho một thứ sẽ nằm trong database suốt ba năm tới. Loại thứ hai thường không có ai chặn lại, bởi vì lúc quyết thì nó trông y hệt một dòng code bình thường.
2. Loại trước rồi mới chấm
Sai lầm thứ hai là ném hết mọi tiêu chí vào cùng một cái bảng và cho chúng cạnh tranh với nhau bằng điểm số. Làm vậy thì một phương án vi phạm điều cấm vẫn có thể thắng, miễn là nó ghi đủ điểm ở mấy tiêu chí khác.
Ràng buộc cứng thì phải là đạt hoặc trượt, không quy ra điểm được.
Mình có một ví dụ rất thật, viết trong bài về chống farm account. Lúc đi tìm cách định danh thiết bị, có ba hướng. Một là UUID sinh ngẫu nhiên lưu trong bộ nhớ ứng dụng, đơn giản nhất nhưng gỡ app là mất sạch. Hai là Keychain trên iOS với SSAID trên Android, sống sót được qua chuyện gỡ ra cài lại. Ba là lấy định danh từ Widevine DRM, bền tới mức sống sót qua cả factory reset.
Nếu mình chấm bằng bảng, với tiêu chí quan trọng nhất là độ bền của định danh, thì Widevine thắng thuyết phục, thắng cách biệt luôn. Cột nào cũng đẹp.
Chỉ có điều cái ID đó bị trùng giữa các máy cùng đời cùng hãng. Đặt vào bài toán chống gian lận thì nghĩa là cả một dòng máy phổ thông bị gom thành một cụm, và người vừa mua máy mới về sẽ bị chặn đăng ký mà chẳng hiểu vì sao.
Đây không phải là một điểm trừ để bù bằng điểm cộng ở chỗ khác. Đây là ràng buộc cứng: không được chặn nhầm người dùng thật. Vi phạm cái đó thì loại, không cần chấm tiếp, dù nó có ăn đứt ở mọi cột còn lại.
Cái gì sai thì loại thẳng, đừng cho nó điểm. Cho điểm là mở đường cho nó thắng.
Nên bước đầu tiên trong mọi lần cân nhắc của mình bây giờ là viết ra danh sách điều kiện loại trước khi viết danh sách tiêu chí chấm. Kiểu như: phải chạy được trên hạ tầng đang có, phải có đường rút lui, không được yêu cầu người dùng cài thêm gì, phải xử lý được ở mức tải hiện tại nhân ba. Phương án nào trượt một điều là ra khỏi bàn ngay, khỏi tốn công chấm.
Danh sách này thường ngắn hơn mình tưởng. Nếu nó dài tới bảy tám dòng thì gần như chắc chắn mình đang nhét mấy thứ chỉ là mong muốn vào chỗ của ràng buộc.
3. Ma trận trọng số, và chỗ nó bị gian lận nhiều nhất
Qua được vòng loại rồi thì mới tới lượt cái bảng. Cách làm thì ai cũng biết: liệt kê tiêu chí, mỗi tiêu chí một trọng số, chấm từng phương án theo thang một tới năm, nhân rồi cộng.
Lấy luôn ví dụ định danh thiết bị ở trên, sau khi Widevine đã bị loại từ vòng ràng buộc, còn lại hai phương án. Điểm dưới đây là mình tự chấm cho bài toán của mình, không phải đo đạc gì cả, và đó chính là chỗ cần cẩn thận nhất.
| Tiêu chí | Trọng số | UUID trong bộ nhớ app | Keychain + SSAID |
|---|---|---|---|
| Không chặn nhầm người thật | 5 | 5 | 5 |
| Bền qua việc gỡ và cài lại | 4 | 1 | 5 |
| Công sức triển khai | 2 | 5 | 2 |
| Rủi ro chính sách store | 3 | 5 | 4 |
| Tổng | 54 | 61 |
Cái bảng này trông có vẻ khách quan. Nó không hề khách quan. Ít nhất có ba chỗ mình có thể lái nó về đâu cũng được.
Chỗ thứ nhất là trọng số được chọn sau khi đã biết đáp án. Đây là kiểu gian lận tinh vi nhất, vì nó không cần nói dối câu nào. Mình chỉ cần nâng trọng số của tiêu chí mà phương án mình thích đang mạnh, hoặc hạ trọng số của chỗ nó yếu. Nếu ở bảng trên mình cho công sức triển khai trọng số 5 thay vì 2, kết quả sẽ đảo chiều, và mình vẫn có một cái bảng đàng hoàng để đem đi họp.
Cách chữa thì rẻ tới mức buồn cười: viết trọng số ra trước khi liệt kê phương án, và viết cho ai đó nhìn thấy. Chỉ cần chốt trọng số lúc trong đầu chưa có ứng viên nào, phần lớn khả năng gian lận biến mất.
Chỗ thứ hai là thang điểm giả vờ tuyến tính. Từ 3 lên 4 ở tiêu chí công sức triển khai nghĩa là đỡ mất vài ngày. Từ 3 lên 4 ở tiêu chí chặn nhầm người thật nghĩa là bớt đi một nhóm người dùng bị đuổi khỏi sản phẩm mà không biết vì sao. Hai cái đó không cùng đơn vị, cộng lại với nhau về mặt toán học là vô nghĩa. Bảng điểm luôn có một lượng vô nghĩa nhất định nằm sẵn trong nó, biết vậy để đừng tin con số tổng quá mức.
Chỗ thứ ba là mấy tiêu chí thực ra chỉ là một. Cái này mình dính nhiều lần. Ngồi liệt kê ra nào là dễ học, nào là tài liệu đầy đủ, nào là cộng đồng lớn, nào là dễ tuyển người. Bốn dòng nhìn rất phong phú, nhưng chúng đo gần như cùng một thứ, và phương án nào mạnh ở đó thì được cộng điểm bốn lần. Trọng số thật của nó bị nhân lên gấp bốn mà không ai nhận ra.
Bảng điểm chỉ có giá trị khi nó có quyền phản đối mình. Sửa trọng số cho ra đáp án mình thích thì đó không còn là chấm điểm nữa, đó là trang trí.
Nói vậy không có nghĩa là bỏ bảng. Giá trị thật của nó không nằm ở con số tổng, mà nằm ở chỗ nó ép mình phải viết ra tiêu chí. Rất nhiều lần mình phát hiện ra mình đang cân nhắc dựa trên một tiêu chí mà chính mình không dám viết ra thành chữ, kiểu như tại vì mình thích công nghệ đó. Viết ra thì thấy ngay là nó không đáng nằm trong bảng.
4. Chấm cái đắt nhất, chứ không phải cái dễ chấm nhất
Có một thiên lệch rất khó tránh: tiêu chí nào dễ ước lượng thì được đưa vào bảng, tiêu chí nào khó ước lượng thì rơi ra ngoài. Mà cái đắt nhất thì thường lại là cái khó ước lượng nhất.
Công sức code thì ai cũng đoán được, sai số cỡ gấp đôi. Còn chi phí sống chung với nó thì mù mờ hơn nhiều. Ai là người bị gọi dậy lúc hai giờ sáng khi nó hỏng. Ai là người nâng phiên bản khi thư viện có lỗ hổng bảo mật. Người mới vào đội mất bao lâu để hiểu nó. Sáu tháng nữa cái thư viện này còn ai duy trì không.
Mình có một mẹo nhỏ để kéo mấy thứ đó vào bảng: thay vì hỏi làm cái này mất bao lâu, hỏi ai sẽ phải sống với nó.
Câu hỏi đó lôi ra được nhiều thứ. Nếu cả đội có đúng một người biết ngôn ngữ đó thì phương án đó phải bị trừ điểm, dù về mặt kỹ thuật nó ưu việt. Nếu vận hành nó cần một loại hạ tầng mà mình chưa từng vận hành bao giờ, thì con số ước lượng của mình cho nó là bịa, và phải ghi rõ là bịa.
Cũng đừng quên thời gian ra mắt là một tiêu chí thật, không phải sự vội vàng. Một phương án tốt hơn hai mươi phần trăm mà chậm hơn hai tháng, đặt trong một sản phẩm đang cần biết là có ai dùng hay không, thì nó là phương án tệ hơn. Hai tháng đó là hai tháng không có phản hồi từ người dùng thật, và với sản phẩm giai đoạn đầu thì đó là thứ đắt nhất mà mình đang tiêu.
5. Chấm theo kịch bản, đừng chấm theo tính từ
Đây là thứ mình thấy đổi được nhiều nhất trong cách mình làm, và nó lấy ý từ mấy phương pháp phân tích đánh đổi kiến trúc kiểu ATAM.
Vấn đề của tiêu chí dạng tính từ, ví dụ như khả năng mở rộng, tính bảo mật, dễ bảo trì, là phương án nào nghe cũng đạt. Ai cũng gật gù, chấm bốn điểm, đi tiếp. Không ai bị lộ ra là mình đang không biết gì cả.
Đổi thành kịch bản cụ thể thì khác hẳn. Không hỏi phương án này có mở rộng tốt không, mà hỏi: tối thứ bảy có đợt khuyến mãi, lượng yêu cầu tăng gấp mười trong mười lăm phút, phương án này lúc đó xảy ra chuyện gì, cái gì gãy trước, gãy rồi thì phục hồi bằng cách nào, mất bao lâu.
Không hỏi phương án này có an toàn không, mà hỏi: giả sử khoá bị lộ ngày hôm nay, mình biết được sau bao lâu, và xoay khoá mất mấy tiếng.
Không hỏi có dễ bảo trì không, mà hỏi: sáu tháng nữa cần thêm một sàn thương mại điện tử nữa vào hệ thống, phải sửa mấy chỗ, có phải đụng vào dữ liệu cũ không.
Chỉ cần ba bốn kịch bản kiểu đó là những chỗ mình đang đoán lộ ra hết. Và cái hay là kịch bản thì kiểm chứng được, còn tính từ thì không.
Tính từ thì phương án nào cũng đạt. Kịch bản thì chỉ cần ba câu là lộ ra chỗ mình đang đoán.
Có một thứ nữa hiện ra khi làm theo kịch bản, mà chấm bằng tính từ thì không bao giờ thấy: những chỗ mà một quyết định kéo hai thuộc tính đi ngược chiều nhau. Thêm một lớp cache thì thời gian phản hồi tốt lên nhưng tính nhất quán xấu đi. Tách service thì triển khai độc lập được nhưng gỡ lỗi khó lên hẳn. Đó mới đúng là chỗ cần người ngồi quyết, và cũng chính là chỗ mà một con số tổng duy nhất che mất hoàn toàn.
6. POC không phải để chứng minh mình đúng
Tới phần POC, và đây là chỗ mình thấy nhiều người làm sai nhất, kể cả mình.
POC hay bị làm theo trình tự ngược: đã chọn xong trong đầu rồi, làm POC để có cái đem đi thuyết phục. Làm kiểu đó thì kết quả biết trước, vì mình sẽ vô thức chọn demo đúng phần mà phương án đó mạnh, và né phần nó yếu. Tốn hai tuần để xác nhận một thứ mình đã tin từ đầu.
POC tử tế thì phải ngược lại hoàn toàn. Trước khi gõ dòng code đầu tiên, viết ra ba thứ.
Thứ nhất, câu hỏi cần trả lời. Đúng một câu, cụ thể, kiểu như cái SSAID này có giữ nguyên sau khi gỡ app cài lại trên máy Android 8 trở lên không. Không phải kiểu phương án này có ổn không.
Thứ hai, điều kiện thất bại, viết ra trước và viết bằng số. Nếu độ trễ ở phân vị 99 vượt quá năm trăm mili giây thì loại. Nếu phải sửa quá ba chỗ trong luồng thanh toán thì loại. Viết trước thì lúc kết quả xấu mình không kịp tự thương lượng với chính mình.
Thứ ba, hạn chót. Ba ngày, năm ngày, bao nhiêu cũng được nhưng phải có. Hết hạn mà chưa trả lời được câu hỏi thì bản thân chuyện đó đã là một câu trả lời rồi.
Và quan trọng nhất: làm phần rủi ro nhất trước, đừng làm phần dễ demo nhất. Bản năng của mình luôn là dựng cái giao diện cho nó ra hình hài trước, vì nhìn sướng. Nhưng cái giao diện đó không trả lời câu hỏi nào cả. Thứ cần đâm vào trước là chỗ mình không biết nó có chạy được hay không.
POC là để giết một phương án nhanh nhất có thể, chứ không phải để nuôi nó.
Còn một cái bẫy nữa, kinh điển tới mức gần như ai cũng dính một lần: POC chạy được rồi thì đem thẳng lên production. Nó chạy được trong điều kiện lý tưởng, không xử lý lỗi, không có người dùng thật, không có dữ liệu bẩn. Cách phòng duy nhất mà mình thấy có tác dụng là ngay từ đầu đã xác định code POC là code sẽ vứt, và nói rõ điều đó ra với mọi người liên quan trước khi bắt đầu.
7. Ghi lại lý do, vì sáu tháng nữa không ai nhớ
Chấm xong, chọn xong, làm xong. Rồi sáu tháng sau có người mở code ra và hỏi tại sao chỗ này làm kiểu kỳ vậy.
Nếu người đó là người khác thì họ sẽ nghĩ người viết ra nó không biết gì. Nếu người đó là chính mình thì cũng nghĩ y vậy thôi, chỉ là tự nghĩ về mình.
Nên mình có thói quen ghi lại, ngắn thôi, một trang là quá đủ. Bối cảnh lúc đó thế nào, đang bị ràng buộc bởi cái gì. Đã cân nhắc những phương án nào, và mỗi phương án bị loại vì lý do gì. Chốt cái nào. Chấp nhận đánh đổi gì.
Nhưng phần giá trị nhất, và cũng là phần hay bị bỏ quên nhất, là dòng cuối: điều kiện xem xét lại.
Bởi vì phần lớn quyết định kiến trúc không sai, chúng chỉ hết hạn. Cái quyết định lưu dữ liệu trong một bảng duy nhất là hoàn toàn đúng ở mức mười nghìn dòng, và hoàn toàn sai ở mức mười triệu dòng. Vấn đề là không ai ngồi canh xem lúc nào thì nó chuyển từ đúng sang sai.
Nên mình cố viết dòng đó cho cụ thể tới mức đo được. Kiểu như: xem lại khi bảng này vượt năm triệu dòng, hoặc xem lại khi có hơn ba đội cùng sửa vào module này. Viết được như vậy thì nó thành một thứ kiểm tra được, chứ không phải một lời hứa suông là để sau tính.
Quyết định kiến trúc hiếm khi sai. Chúng chỉ hết hạn, mà không có ai báo.
8. Mấy cái bẫy mình đã dính, kể ra cho đỡ tiếc
Neo vào phương án nói ra đầu tiên. Ai mở miệng trước thì phương án của người đó thành mốc, mấy phương án sau đều bị đem ra so với nó thay vì so với yêu cầu. Cách chữa rẻ nhất mà mình biết là mỗi người viết ra ý của mình trước khi ai đó nói ra thành lời.
Tiếc công đã bỏ vào POC. Bỏ hai tuần làm POC rồi thì rất khó nói câu bỏ đi, kể cả khi kết quả đã nói rõ là nên bỏ. Hai tuần đó tiêu rồi, có giữ hay bỏ thì cũng không lấy lại được, nó không nên có mặt trong phép cân đo nữa. Biết vậy mà vẫn khó, nên tốt nhất là viết điều kiện thất bại ra từ đầu, để lúc đó chỉ còn việc đọc lại chứ không phải ngồi quyết.
Cho điểm cao vì nó mới. Cái mới luôn được cộng điểm ở một tiêu chí mơ hồ tên là tương lai, trong khi thứ đáng hỏi là công nghệ này đã có ai chạy thật ở quy mô giống mình chưa, và lúc nó hỏng thì tìm câu trả lời ở đâu.
Dùng bảng điểm để né trách nhiệm. Cái này là cái tệ nhất. Có bảng rồi thì sai không phải lỗi mình, tại bảng nó ra vậy. Nhưng bảng không quyết, người quyết. Bảng chỉ là cách mình trình bày suy nghĩ cho có thứ tự.
Tin vào số lẻ. Bốn phẩy hai so với ba phẩy chín thì không có nghĩa gì hết, vì đầu vào của phép tính đó là mấy con số mình tự chấm bằng cảm giác. Mình đặt cho mình một quy ước: chênh nhau dưới mười phần trăm thì coi như hoà. Mà hoà thì không chọn cái điểm cao hơn, chọn cái nào rút lui rẻ hơn.
Kết bài
Viết một hồi thì mình nhận ra bài này không có phương pháp nào biến quyết định kiến trúc thành phép tính cả, và mình nghĩ cũng không nên có.
Chấm điểm không phải để tìm ra đáp án đúng. Nó là để ép mình nói rõ mình đang đánh đổi cái gì lấy cái gì, và để sáu tháng sau còn có chỗ mà đọc lại. Cái giá trị nằm ở quá trình bị buộc phải viết ra, chứ không nằm ở con số cuối bảng.
Nếu bỏ hết chỉ giữ lại ba thứ, thì mình giữ ba thứ này.
Một, trước khi cân đo bất cứ thứ gì, hỏi xem quyết định này quay đầu tốn bao nhiêu. Rẻ thì làm luôn đừng bàn, đắt thì mới ngồi xuống cho tử tế.
Hai, viết ràng buộc cứng ra trước khi nhìn vào bất kỳ phương án nào. Cái gì vi phạm thì loại thẳng, đừng cho nó cơ hội gỡ điểm ở chỗ khác.
Ba, khi hai phương án ngang ngửa thì đừng cố tìm cách phân định thắng thua. Chọn cái nào sai rồi rút ra rẻ hơn.
Còn cái bảng điểm hôm đó, cuối cùng mình không sửa trọng số. Mình để nguyên, và đi theo phương án nó chỉ ra. Không phải vì mình tin cái bảng, mà vì lúc ngồi nhìn hai cột số cạnh nhau, mình mới thấy rõ là lý do mình thích phương án kia chỉ là vì mình đã đọc về nó cả tuần trước đó rồi.
Đó mới là thứ cái bảng làm được cho mình. Không phải chọn giùm, mà chỉ ra chỗ mình đang tự lừa mình.
Và thôi đến đây thôi, chúc các bạn có những có những dòng code thật nà chất nượng! Xin chào và hẹn gặp lại các bạn ở những bài viết sau