Các cuộc tranh luận về phiên bản API hầu như luôn bắt đầu từ đầu sai, tức là từ chỗ đặt số hiệu phiên bản. Đó là quyết định ít hệ quả nhất trong toàn bộ chủ đề. Điều thực sự quan trọng là thay đổi nào mới cần đến một phiên bản mới, và phần lớn đội ngũ sai lầm theo hướng chủ quan: họ phát hành thứ mà họ tin là chỉ bổ sung, rồi một client hỏng.
Mô hình tư duy hữu ích là API của bạn là một lời hứa về những gì bên gọi có thể dựa vào. Một thay đổi phá vỡ tương thích nếu nó làm mất hiệu lực điều mà một bên gọi hợp lý đang dựa vào, và các bên gọi dựa vào nhiều hơn những gì tài liệu của bạn cho phép.
Thay đổi khiến ai cũng dính: thêm một trường vào phản hồi. Nó chỉ bổ sung, nó không thể làm hỏng một client viết tốt, và nó vẫn thường xuyên làm hỏng các client thật, vì một số trong đó kiểm tra phản hồi rất chặt và từ chối những trường lạ. Lỗi thuộc về bạn hay về họ đã không còn ý nghĩa gì kể từ lúc tích hợp của họ ngừng chạy và điện thoại đổ chuông.
Thế nào mới thực sự là phá vỡ tương thích
Phá vỡ rõ ràng: xóa hoặc đổi tên một trường, đổi kiểu dữ liệu của một trường, thêm tham số bắt buộc vào yêu cầu, siết chặt kiểm tra dữ liệu, đổi ý nghĩa của một giá trị đang có, hoặc đổi mã trạng thái mà bên gọi dùng để rẽ nhánh.
Phá vỡ trên thực tế, dù trông có vẻ an toàn: thêm một trường vào phản hồi khi client kiểm tra chặt. Đổi thứ tự của một mảng vốn chưa từng được cam kết nhưng bên gọi vẫn mặc định. Đổi một thông báo lỗi mà ai đó đang so khớp theo chuỗi. Biến một thao tác đồng bộ thành bất đồng bộ.
An toàn thật sự: thêm tham số yêu cầu tùy chọn mới, thêm một endpoint mới, nới lỏng kiểm tra dữ liệu, và thêm giá trị mới vào một danh sách liệt kê, nhưng chỉ khi client đã được báo trước là phải chấp nhận giá trị lạ và bạn kiểm chứng được rằng họ xử lý đúng.
Quy luật ở đây là mức an toàn phụ thuộc vào việc bên gọi thực sự làm gì, chứ không phải vào việc đặc tả cho phép gì. Nếu bạn kiểm soát mọi client, bạn có thể kiểm chứng. Nếu không, hãy giả định rằng có người đang dựa vào đúng thứ bạn cho là phụ, vì đúng là như vậy.
Các chiến lược phiên bản API và cái giá của chúng
Phiên bản trong URL. Cách phổ biến nhất, và ưu điểm là nó hiển nhiên: /v1/orders và /v2/orders là hai tài nguyên khác nhau ngay trước mắt. Cái giá là nó khuyến khích nâng phiên bản cho toàn bộ API, nên một thay đổi ở một endpoint kéo theo mọi thứ, và client phải chuyển đổi cùng lúc.
Phiên bản trong header. Giữ URL ổn định và cho phép chia nhỏ hơn, đổi lại là vô hình. Không ai thấy phiên bản trên trình duyệt hay trong một dòng log nếu không đi tìm, còn bên gọi bỏ qua header sẽ nhận mặc định của bạn, một quyết định bạn phải chọn có chủ đích.
Phiên bản theo ngày. Bên gọi ghim một ngày và nhận API đúng như nó hoạt động khi đó. Stripe ghi rõ cách làm này: mỗi tài khoản có một phiên bản mặc định và từng yêu cầu có thể ghi đè. Nó cho bước chuyển đổi nhỏ nhất có thể và dồn gánh nặng tương thích vào chính mã nguồn của bạn, nơi phải duy trì các phép chuyển đổi giữa các phiên bản.
Không phiên bản, chỉ thêm mới. Khả thi và bị đánh giá thấp, khi bạn thật sự cam kết được là sẽ không bao giờ gỡ bỏ thứ gì. Cái giá là những trường không thể xóa và hành vi không thể sửa, một thứ thuế chậm thay vì một hóa đơn gấp.
Không có câu trả lời đúng, chỉ có sự đánh đổi giữa gánh nặng chuyển đổi của bạn và của khách hàng. Phiên bản theo ngày dễ chịu nhất cho bên gọi và tốn kém nhất để vận hành. Phiên bản trong URL thì ngược lại.
Làm cho thay đổi phá vỡ trở nên chịu được
Mở rộng trước, thu hẹp sau. Thêm trường mới bên cạnh trường cũ. Ghi dữ liệu vào cả hai. Cho client thời gian chuyển sang. Gỡ trường cũ ở một phiên bản sau. Cách này biến một thay đổi phá vỡ thành hai thay đổi an toàn, và bước phụ đó gần như lúc nào cũng đáng giá.
Đo xem ai đang dùng gì. Bạn không thể gỡ bỏ một phiên bản một cách an toàn nếu không biết ai còn nằm trên đó. Hãy ghi log phiên bản và danh tính client ở mọi yêu cầu, để việc thông báo ngừng hỗ trợ trở thành cuộc trao đổi dựa trên bằng chứng chứ không phải một lời rao vào bóng tối.
Thông báo bằng cơ chế, không chỉ bằng email. Hai header phản hồi Deprecation và Sunset cho phép client tự phát hiện ngày ngừng hỗ trợ bằng chương trình, và điều đó dễ được để ý hơn một tin nhắn gửi tới địa chỉ không còn ai đọc.
Cho một khoảng thời gian thực tế. Các tích hợp được bảo trì bởi những người có ưu tiên khác, nên một hạn chót ngắn hơn chu kỳ phát hành của họ sẽ bị bỏ lỡ. Sáu tháng là mức thường thấy với một API công khai; ngắn hơn vẫn hợp lý với một nhóm nhỏ đối tác quen mà bạn đã trao đổi.
Gỡ bỏ một phiên bản cũ
Chính phép đo ở trên mới làm cho bước này khả thi. Hãy công bố ngày, theo dõi lưu lượng giảm dần, và liên hệ đích danh những bên gọi chưa chuyển.
Hãy chuẩn bị cho một cái đuôi dài. Sẽ có những tích hợp mà không ai bên phía khách hàng còn nhớ là của mình, và chúng chỉ lộ ra vào lúc bạn tắt phiên bản. Ngắt tạm thời có ích ở đây: hãy vô hiệu hóa phiên bản cũ trong thời gian ngắn vào các khung giờ đã báo trước ngày cuối, để sự cố xảy ra khi có người đang chờ nó chứ không phải giữa lúc cao điểm.
Cách nhìn thành thật là một số bên gọi chỉ chuyển khi phiên bản cũ ngừng hoạt động. Hãy tính trước điều đó thay vì bất ngờ, và bảo đảm rằng khi hỏng thì họ nhận được một lỗi rõ ràng giải thích chuyện gì đã xảy ra, chứ không phải một lần hết thời gian chờ.
Phiên bản bạn không cần
Phần lớn API nội bộ không cần phiên bản gì cả, vì bạn kiểm soát mọi bên gọi và có thể đổi cả hai phía cùng lúc. Thêm cơ chế thương lượng phiên bản vào một giao diện chỉ dùng giữa hai dịch vụ của chính bạn là bộ máy tốn kém mà không bảo vệ được điều gì.
Thời điểm nó trở nên cần thiết là khi bạn không còn triển khai được tất cả bên tiêu thụ cùng lúc, dù vì họ thuộc đội khác, chu kỳ phát hành khác hay công ty khác. Đó mới là yếu tố kích hoạt thật sự, và nó không liên quan gì đến việc API công khai đến đâu. Các cân nhắc thiết kế trong hướng dẫn API serverless của chúng tôi áp dụng y hệt ở biên.
Mecanik thiết kế và bảo trì những API kiểu này như một phần công việc phát triển phần mềm. Sơ đồ đánh số phiên bản hiếm khi là quyết định thú vị; biết được bên gọi nào của bạn còn nằm trên bản cũ thì luôn luôn là.
Câu hỏi thường gặp
Thế nào là một thay đổi API phá vỡ tương thích? Xóa hoặc đổi tên một trường, đổi kiểu dữ liệu, thêm tham số bắt buộc, siết chặt kiểm tra dữ liệu, đổi ý nghĩa của một giá trị, hoặc đổi mã trạng thái mà bên gọi dùng để rẽ nhánh. Trên thực tế cũng phá vỡ: thêm một trường phản hồi khi client kiểm tra chặt, đổi thứ tự mảng mà bên gọi vẫn mặc định, và sửa nội dung thông báo lỗi mà ai đó đang so khớp.
Nên đặt phiên bản API trong URL hay trong header? Phiên bản trong URL thì dễ thấy và đơn giản, nhưng khuyến khích nâng phiên bản cho cả API và buộc client chuyển đổi mọi thứ cùng lúc. Phiên bản trong header giữ URL ổn định và cho phép chia nhỏ hơn, nhưng vô hình trong log lẫn trình duyệt, và đòi hỏi một giá trị mặc định được chọn có chủ đích cho những bên gọi bỏ qua nó. Không cách nào sai; chúng chỉ đổi gánh nặng chuyển đổi của bạn lấy gánh nặng của khách hàng.
Phiên bản API theo ngày là gì? Bên gọi ghim một ngày và nhận API đúng như nó hoạt động khi đó, giống cách Stripe làm với một phiên bản mặc định cho mỗi tài khoản mà từng yêu cầu có thể ghi đè. Cách này cho client bước chuyển đổi nhỏ nhất có thể và dồn gánh nặng tương thích vào mã nguồn của bạn, nơi sau đó phải duy trì các phép chuyển đổi giữa các phiên bản.
Thời gian thông báo ngừng hỗ trợ API nên dài bao lâu? Sáu tháng là mức thường thấy với một API công khai, và ngắn hơn vẫn hợp lý với một nhóm nhỏ đối tác quen mà bạn đã trao đổi trực tiếp. Các tích hợp được bảo trì bởi những người có ưu tiên khác, nên một khoảng thời gian ngắn hơn chu kỳ phát hành của họ sẽ bị bỏ lỡ dù thông báo có rõ ràng đến đâu.
API nội bộ có cần phiên bản không? Thường là không, vì bạn kiểm soát mọi bên gọi và có thể đổi cả hai phía cùng lúc. Phiên bản trở nên cần thiết ở thời điểm bạn không còn triển khai được tất cả bên tiêu thụ cùng lúc, dù họ thuộc đội khác, chu kỳ phát hành khác hay công ty khác. Đó mới là yếu tố kích hoạt, không phải việc API có công khai hay không.
Bình luận