2 điểm bởi GN⁺ 2024-02-17 | 2 bình luận | Chia sẻ qua WhatsApp

Khi API không làm gì cả, hãy không làm gì cho đúng cách

  • Khi API không được phép làm gì cả, điều quan trọng là phải bảo đảm nó “không làm gì” theo đúng cách.
  • Ví dụ, Windows có một hạ tầng in ấn rất đồ sộ, nhưng Xbox thì không có hạ tầng như vậy.
  • Khi ứng dụng cố in trên Xbox, việc ném ra NotSupportedException là cách làm sai.
  • Vì ứng dụng chủ yếu được kiểm thử trên PC, nên khi chạy trên Xbox có thể không xử lý ngoại lệ này và bị sập.
  • Một thiết kế tốt hơn để “hỗ trợ” tính năng in trên Xbox là để hàm in vẫn thành công nhưng báo rằng không có máy in nào được cài đặt.
  • Khi người dùng thử in, hệ thống yêu cầu chọn máy in nhưng danh sách trống, nên người dùng sẽ nhận ra rằng “à, không có máy in” và hủy yêu cầu in.
  • Với các ứng dụng cố cài máy in, hàm cài đặt máy in có thể trả về ngay với mã kết quả “người dùng đã hủy thao tác”.
  • Mục tiêu là hành xử như thể tính năng in được hỗ trợ đầy đủ, nhưng trên thực tế thì như thể không có máy in nào.
  • Trên các hệ thống mà việc in hoàn toàn không hoạt động, có thể thêm một hàm kiểm tra khả năng in để ẩn nút in khỏi UI.
  • Kiểu hành vi này được gọi là “vô hiệu” (inert).
  • Bề mặt API vẫn tồn tại và vẫn hoạt động theo đặc tả, nhưng trên thực tế không làm gì cả.
  • Điều quan trọng là nó phải “không làm gì” một cách nhất quán với tài liệu, để giảm thiểu vấn đề với mã hiện có.

Ví dụ về vô hiệu hóa API

  • Có một ví dụ về việc vô hiệu hóa một API bao gồm nhiều hàm tạo widget handle, các hàm nhận widget handle và một hàm đóng widget handle.
  • Ban đầu, nhóm đề xuất vô hiệu hóa API bằng cách để CreateWidget thành công nhưng trả về con trỏ null.
  • Tuy nhiên, cách này có thể khiến ứng dụng bối rối. “Lời gọi thành công nhưng lại không nhận được handle hợp lệ sao?”
  • Việc để EnableWidget trả về “handle không hợp lệ” cũng có thể gây nhầm lẫn.
  • Họ đã tìm thấy trong tài liệu hiện có một giá trị trả về ERROR_CANCELLED, nghĩa là việc tạo widget đã bị người dùng hủy.
  • Vì vậy, mỗi khi ứng dụng cố tạo widget, có thể trả lời rằng “không, người dùng đã hủy”.

Ý kiến của GN⁺

  • Điều quan trọng nhất trong bài viết này là khi API không làm gì cả, nó vẫn phải “không làm gì” theo cách không làm tổn hại trải nghiệm người dùng và vẫn giữ được khả năng tương thích với mã hiện có.
  • Cách tiếp cận này nhấn mạnh với các lập trình viên tầm quan trọng của thiết kế API, đồng thời góp phần mang lại trải nghiệm phần mềm thân thiện hơn với người dùng.
  • Bài viết cho thấy một khía cạnh tinh tế của kỹ nghệ phần mềm, và đưa ra một ví dụ thú vị về cách ứng dụng có thể thất bại một cách duyên dáng ngay cả trong những môi trường không ngờ tới.

2 bình luận

 
GN⁺ 2024-02-17
Ý kiến Hacker News
  • Ý kiến về việc "nuốt lỗi":

    • Che giấu lỗi là một thực hành không tốt.
    • Nó khiến việc tìm ra bug và kiểm thử khó hơn bằng cách che đậy khuyết điểm của phần mềm thay vì giải quyết vấn đề.
    • panic của Go là một cách tốt để làm nổi bật sai lầm của lập trình viên trong quá trình kiểm thử.
    • Nếu các actor trong hệ thống cố che giấu lỗi của mình thì việc xác định và khắc phục vấn đề sẽ trở nên khó hơn rất nhiều.
  • Ý kiến về khả năng tương thích ngược:

    • Khả năng tương thích ngược luôn là một công việc lộn xộn, và lựa chọn thường là hoặc làm không hoàn hảo hoặc không làm gì cả.
    • Đây là lý do khi nhấp vào tệp Word '97 hoặc tệp game cho MS-DOS, chúng vẫn mở như mong đợi trên máy tính ngày nay.
  • Phàn nàn về thiết kế UI:

    • UI gợi ý những thiết bị có thể tồn tại là cực kỳ khó chịu.
    • Nó khiến người dùng lãng phí thời gian để tìm ra những thiết bị thực ra không được hỗ trợ.
  • Chỉ ra việc Microsoft không rút kinh nghiệm:

    • Sau 30 năm, Microsoft vẫn lặp lại cùng một sai lầm.
    • Việc ứng dụng hiển thị một danh sách trống khi cố tìm máy in chỉ là lặp lại vấn đề cũ.
  • Ý kiến về việc Xbox không hỗ trợ in:

    • Xbox không hỗ trợ in là vì Microsoft đã định nghĩa như vậy.
    • Về phần cứng, nó có cùng khả năng in như các thiết bị Windows khác.
    • Những "giải pháp" kiểu này là vấn đề do các quyết định phi lý của Microsoft gây ra.
  • Lời khuyên về việc sử dụng API:

    • Đúng là component nên gặp vấn đề trước người dùng, nhưng không thể đồng ý với cách tác giả diễn đạt điều đó.
    • Việc hàm in ném ra NotSupported­Exception không phải là sai.
    • Điều tác giả mô tả là một kiểu hack để hỗ trợ các client kém chất lượng.
  • Cảm nhận về "tuân thủ ác ý":

    • Vừa thích vừa không thích cách xử lý vấn đề này.
    • Tuy nhiên, nếu mục tiêu là để nhiều người dùng hơn chạy được nhiều phần mềm hơn trên nền tảng, thì đây là một cách làm tốt.
  • Ý kiến tích cực về bảo mật:

    • Việc bỏ qua đúng cách các lệnh gọi API giúp ngăn chương trình thực hiện những hành vi có hại hơn.
  • Hồi tưởng về chiến lược trình duyệt:

    • Chiến lược cố gắng hiển thị trang tốt nhất có thể ngay cả khi mã HTML có lỗi từng được xem là một chiến lược tốt.
    • Người dùng không muốn lỗi, và lẽ ra chúng ta nên học được điều đó từ trải nghiệm này.
  • Điều các nhà phê bình về xử lý ngoại lệ đang hiểu sai:

    • Có một điểm mà các nhà phê bình đã bỏ lỡ trong những tình huống không cần phải ném ngoại lệ.
    • Nếu thiết bị (máy in) chưa được kết nối, ứng dụng nên xử lý tình huống đó gọn gàng thay vì bị crash.
 
Ý kiến trên Lobste.rs
  • Ý ở đây là các chức năng in hoạt động nhất quán như thể việc in được hỗ trợ hoàn toàn, nhưng kỳ lạ là sẽ không bao giờ có máy in thật để in, và như vậy thì giải thích được khá nhiều điều
    Nói đùa vậy thôi, tôi không đồng ý với kiểu lập trình và trải nghiệm người dùng phòng thủ quá mức như thế này. Làm vậy thì phần mềm sẽ không làm đúng việc của nó mà người dùng cũng không biết vì sao, lại càng không có cách nào để tìm ra. Ứng dụng nên bắt lỗi và nếu có thể thì tạo thông báo thân thiện với người dùng, nếu không thì ít nhất cũng phải hiển thị thông báo lỗi gốc cho người dùng. Nếu là tác vụ nền thì phải có nhật ký lỗi
    Tôi công nhận bài này được viết từ góc nhìn của nhà phát triển API chứ không phải nhà phát triển ứng dụng. Vậy thì cần tài liệu hóa lỗi API và cung cấp thông báo lỗi để phía gọi có thể xử lý
    Tôi cũng không thích việc ẩn nút trong UI chỉ vì không có quyền truy cập. Nếu không gian cho phép thì tốt hơn nên hiện nút nhưng vô hiệu hóa nó, và khi người dùng rê chuột vào thì hiển thị thông điệp cho biết làm thế nào để kích hoạt nó
    • Cũng cần lưu ý rằng bài này không chỉ từ góc nhìn của một nhà phát triển API bình thường mà là của nhà phát triển Windows API. Từ lâu Microsoft đã giữ lập trường rằng Windows API không được phá vỡ khả năng tương thích chỉ vì phiên bản thay đổi
    • Đây là một cuộc thảo luận điển hình về Định luật Postel / nguyên tắc tính bền vững. Giờ thì ai cũng biết nguyên tắc tính bền vững đã lỗi thời và đã sinh ra những quái vật như HTML hay các giao thức/định dạng tệp khổng lồ khiến việc viết parser đúng đắn trở nên khó khăn
      Nhìn chung, yêu cầu tính chính xác vẫn tốt hơn. Tuy nhiên, nếu bạn đã có 1 tỷ người dùng hiện hữu thì việc tránh làm hỏng họ nhiều nhất có thể là lựa chọn rất khôn ngoan, và từ góc nhìn người dùng thì cứ chạy được là tạo ra giá trị thực ở cấp hệ thống. Cuối cùng, thái độ nên là thất bại sớm, nhưng đừng để thất bại hàng loạt
    • Trong trường hợp này, có vẻ một trong các API hiện có không có lỗi được tài liệu hóa, nên rất có thể ngay từ đầu đã không có xử lý lỗi. Nếu không muốn làm hỏng toàn bộ phần mềm hiện có thì phải nói dối vì mục đích tốt
      Chuyện này gần với độ ổn định ABI hơn là API. Trên Windows, phần mềm được build từ 15 năm trước vẫn phải tiếp tục chạy được trên hệ điều hành mới càng nhiều càng tốt. Bạn không thể thay đổi chữ ký hàm, nên nếu muốn những API không còn ý nghĩa nữa vẫn tiếp tục hoạt động thì phải dùng những lời nói dối thiện ý
      Ví dụ, API vẫn giả vờ như Active Desktop còn tồn tại, vì lựa chọn khác là làm hỏng hàng loạt phần mềm cũ đang tồn tại
    • Tôi thật sự đồng ý. Không có gì bực bội bằng việc loay hoay tìm một nút mà vì lý do nào đó không hiện trên màn hình của tôi, trong khi người ngồi bên cạnh giải thích hoặc trong tutorial lại thấy nó
      Khi đó bạn không thể biết là tính năng ấy đã biến mất hay chỉ bị chôn ở một màn hình khác trong lúc đó
    • Đúng là ứng dụng nên bắt lỗi và hiển thị cho người dùng.
      Nhưng nếu ứng dụng không làm vậy, thì người dùng ứng dụng đó sẽ đổ lỗi cho Windows. Họ đổ lỗi cho Windows chứ không phải cho ứng dụng, và ngay cả khi ứng dụng bị crash cũng thế
      Vì vậy Microsoft mới tạo ra các giải pháp vòng vo. Cứ để tác vụ in tự biến mất thì dễ hơn nhiều, và rồi người dùng sẽ nghĩ một lát rồi chấp nhận rằng “à đúng rồi, mình đâu có máy in”
  • Nếu hàm cài đặt máy in có thể trả về ngay lập tức và trả mã kết quả là người dùng đã hủy thao tác, thì từ góc độ hỗ trợ ứng dụng, gần như chắc chắn điều đó sẽ dẫn đến hành vi ngoài ý muốn còn khó xử lý hơn nhiều so với việc API in ném ngoại lệ ngay từ đầu
  • Dạo này tôi dùng lập trình có trợ giúp bởi AI khá nhiều, và thấy agent thường hay chèn các kiểm tra if để kiểm tra null. Mỗi lần thấy vậy tôi lại nhớ đến bài này
    Vì thế tôi đã bảo agent đừng lặp đi lặp lại kiểm tra null, mà hãy dùng các hàm vô hại và chỉ xác nhận một lần tại thời điểm khai báo rằng giá trị chắc chắn không bao giờ là null