Khi một ứng dụng còn nhỏ, chúng ta thường sửa API khá thoải mái: đổi tên field, thêm tham số, thay đổi format response. Nhưng khi API đã có web frontend, mobile app, đối tác tích hợp hoặc nhiều service nội bộ cùng dùng, mỗi thay đổi đều có thể làm “gãy” client. API versioning là cách quản lý sự thay đổi đó có kỷ luật hơn. Nói ngắn gọn, API không chỉ là đoạn code trả JSON. Nó là một “hợp đồng” giữa backend và client: endpoint nào tồn tại, request cần gì, response trả gì, lỗi có dạng nào. Khi hợp đồng thay đổi, client cần thời gian để thích nghi. Một ví dụ thực tế Giả sử API hiện tại trả thông tin người dùng: GET /api/users/123 Response: { "id": 123, "name": "Nguyen An", "avatar": "https://..." } Sau này đội sản phẩm muốn tách name thành firstName và lastName để phục vụ màn hình mới. Nếu backend đổi thẳng response thành: { "id": 123, "firstName": "An", "lastName": "Nguyen", "avatar": "https://..." } Frontend cũ đang đọc user.name có thể hiển thị trống hoặc lỗi. Với web có thể deploy lại nhanh, nhưng mobile app thì người dùng không phải lúc nào cũng cập nhật ngay. Đây là lúc cần chiến lược versioning. Những cách version API phổ biến 1. Version trong URL Ví dụ: /api/v1/users/123 /api/v2/users/123 Cách này dễ hiểu, dễ debug, dễ cấu hình routing và log. Nhược điểm là URL có thể trở nên “nặng” nếu thay đổi nhỏ cũng tạo version mới. Đây là lựa chọn thực dụng cho nhiều team, đặc biệt khi API phục vụ mobile app hoặc public client. 2. Version bằng header Ví dụ client gửi: Accept: application/vnd.myapp.v2+json Cách này giữ URL sạch hơn, phù hợp với những hệ thống muốn quản lý content negotiation chặt chẽ. Tuy nhiên nó kém trực quan hơn khi test bằng trình duyệt hoặc chia sẻ link API cho người mới. 3. Version theo từng field hoặc capability Thay vì tạo v2 ngay, backend có thể thêm field mới mà vẫn giữ field cũ: { "id": 123, "name": "Nguyen An", "firstName": "An", "lastName": "Nguyen" } Client mới dùng firstName/lastName, client cũ vẫn dùng name. Sau một thời gian, khi chắc chắn không còn client cũ, field name mới được loại bỏ. Cách này phù hợp với thay đổi nhỏ và có tính tương thích ngược. Nguyên tắc quan trọng: ưu tiên backward compatible Không phải thay đổi nào cũng cần tạo version mới. Một số thay đổi thường an toàn hơn: - Thêm field mới vào response. - Thêm endpoint mới. - Thêm giá trị mới nếu client đã xử lý trường hợp không nhận diện được. - Thêm tham số query không bắt buộc. Một số thay đổi dễ gây lỗi: - Đổi tên hoặc xóa field. - Đổi kiểu dữ liệu, ví dụ id từ number sang string. - Đổi format ngày giờ. - Thay đổi ý nghĩa của một field cũ. - Đổi cấu trúc lỗi mà client đang phụ thuộc. - Biến một field tùy chọn thành bắt buộc. Nếu có thể, hãy thiết kế API theo hướng “client cũ vẫn chạy được”. Ví dụ muốn đổi avatar thành object chi tiết hơn, có thể làm: { "avatar": "https://...", "avatarInfo": { "url": "https://...", "width": 256, "height": 256 } } Thay vì đổi avatar từ string sang object ngay lập tức. Khi nào nên tạo version mới? Nên cân nhắc version mới khi thay đổi phá vỡ hợp đồng cũ và không thể duy trì tương thích một cách sạch sẽ. Ví dụ: - Thiết kế lại response của nhiều endpoint. - Thay đổi mô hình phân quyền hoặc logic nghiệp vụ cốt lõi. - Chuẩn hóa lại format lỗi trên toàn hệ thống. - Gộp/tách tài nguyên khiến client cũ không thể dùng chung. Tuy nhiên, đừng tạo version mới quá thường xuyên. Nếu mỗi thay đổi nhỏ đều sinh ra v2, v3, v4, backend sẽ phải bảo trì nhiều nhánh logic, test case tăng lên và tài liệu dễ rối. Lập kế hoạch ngừng hỗ trợ phiên bản cũ Versioning không chỉ là tạo /v2. Bạn cũng cần kế hoạch cho /v1: - Ghi log hoặc metric để biết còn client nào dùng version cũ. - Thông báo rõ thời gian ngừng hỗ trợ nếu có đối tác hoặc ứng dụng bên ngoài. - Giữ tài liệu riêng cho từng version. - Trả cảnh báo deprecation nếu phù hợp, ví dụ qua response header hoặc field metadata. - Không xóa version cũ khi chưa có dữ liệu cho thấy việc đó an toàn. Với mobile app, cần đặc biệt cẩn thận vì người dùng có thể giữ app cũ trong nhiều tháng. Nếu bắt buộc ngừng hỗ trợ, ứng dụng nên có màn hình yêu cầu cập nhật thay vì chỉ lỗi trắng. Lời khuyên áp dụng cho dự án thực tế - Ngay từ đầu, hãy thống nhất nguyên tắc: thay đổi nào được xem là breaking change. - Nếu API phục vụ nhiều loại client, cân nhắc đặt version trong URL cho dễ vận hành. - Thêm field mới thay vì sửa/xóa field cũ khi có thể. - Viết test contract cho các response quan trọng để tránh vô tình làm gãy client. - Đừng để frontend tự suy đoán quá nhiều về dữ liệu; format lỗi và trạng thái nên được quy ước rõ. - Theo dõi version đang được sử dụng trong log để có cơ sở ra quyết định. API versioning không phải để làm hệ thống phức tạp hơn, mà để thay đổi có kiểm soát. Một API tốt không chỉ chạy đúng hôm nay, mà còn cho phép sản phẩm phát triển ngày mai mà không kéo theo hàng loạt lỗi khó đoán ở client.